Files
gree-controller/BUILD_REPORT.md
T
2026-08-28 09:44:39 +02:00

610 lines
52 KiB
Markdown

# GREE Controller v0.8.3 - build and validation report
## 0.8.3 Temporary Quick Thermostat sessions
Source baseline: v0.8.2.
### Changes
- Added a separate persisted Temporary Quick Thermostat session, independent of the existing `local_thermostat_resume_at` 15-minute local-OFF hand-back mechanism.
- Added finish rules for duration, absolute date/time, temperature reached, continuously held temperature and next schedule boundary.
- Temperature rules support `within`, `at_or_below` and `at_or_above`, configurable tolerance, continuous hold time and an optional maximum safety duration.
- Added active-session countdown/status in the dashboard Quick Thermostat card and a configuration dialog for starting or updating a session.
- Moved the zone automation enabled switch outside the Quick Thermostat controls to separate automation availability from local thermostat operation.
- A temporary session can temporarily enable a disabled zone and restores the pre-session enabled state when it ends.
- Manual Quick Thermostat power changes cancel the temporary session, while target changes update temperature-based session targets safely.
- Bumped controller and Home Assistant integration version to 0.8.3 and rotated the PWA service-worker cache key.
### Validation executed in this environment
- `node --check web/app.js`
- `node --check web/theme-init.js`
- `node --check web/sw.js`
- parsed all JSON files
- `python3 -m compileall -q home-assistant scripts make_zip.py`
- `bash -n scripts/*.sh`
- checked version consistency across Cargo.toml, Cargo.lock, README and Home Assistant manifest
- regenerated and verified `FILE_MANIFEST.sha256`
- tested the final ZIP with `unzip -t`
### Rust toolchain note
This execution environment does not contain `cargo`, `rustc` or `rustfmt`, so Rust tests and full compilation cannot be executed here. The installer/updater still runs `cargo test --all-targets` before replacing the running service.
---
# GREE Controller v0.8.2 - build and validation report
## 0.8.2 UI/i18n + Home Assistant sensor freshness
Source baseline: v0.8.1.
### Changes
- Fixed the 0.8.x ownership translations: `zones.controlOwner`, owner labels and owner timing labels are now inside each language pack's `translations` object, so the Web UI receives translated values instead of raw translation keys.
- Simplified the Polish owner caption from `Właściciel sterowania` to `Sterowanie` and allowed the owner/timing line to wrap instead of truncating.
- Added `home_assistant.sensor_stale_after_seconds` with a serde-compatible 300 s default. The Home Assistant / Sensors page exposes it as minutes (1-1440 min); the backend clamps API/env values to 30-86400 s.
- The configured freshness limit is used by room HA sensors, the outdoor HA sensor and the HA connection test. Existing non-default `zone.sensor_stale_after_seconds` values remain per-zone API overrides; the historical hidden/default 300 s value inherits the global HA setting.
- Added `HA_SENSOR_STALE_AFTER_SECONDS` as an optional environment override.
- Quick Thermostat and Direct Device dashboard grids now use `align-items:start`, so a taller manual/ownership panel no longer visually stretches neighbouring cards in the same row.
- `build.rs` now rejects unexpected top-level language-pack keys, preventing translation keys from being accidentally stored outside `translations` again.
- Added a Rust regression test for global/per-zone stale-timeout resolution.
- Bumped controller and Home Assistant integration version to 0.8.2 and changed the service-worker cache key.
### Validation executed in this environment
- `node --check web/app.js`
- `node --check web/theme-init.js`
- `node --check web/sw.js`
- parsed all JSON files
- verified both language packs contain only `meta` + `translations`
- verified all 564 statically referenced UI translation keys exist in both English and Polish packs
- parsed `web/index.html`
- `python3 -m compileall -q home-assistant scripts make_zip.py`
- `bash -n scripts/*.sh`
- checked version consistency across Cargo.toml, Cargo.lock, README and Home Assistant manifest
- checked that no HA temperature read still uses a hardcoded `Some(300)` freshness limit
- reviewed the complete 0.8.1 -> 0.8.2 source diff for unintended file changes
### Rust toolchain note
This execution environment does not contain `cargo`, `rustc` or `rustfmt`, so the Rust regression test and full compilation cannot be executed here. The existing installer/updater still runs `cargo test --all-targets` before touching the running service and then performs the release build. If Rust compilation or tests fail on the target host, the running service is not replaced.
---
# GREE Controller v0.8.1 - build and validation report
## 0.8.1 Rust borrow-checker hotfix
Version 0.8.1 is a compile-only hotfix for the 0.8.0 ownership release. It does not change controller behavior. Two mode-selection values in `src/engine.rs` borrowed `zone.mode` as `&str` across `refresh_control_ownership(&mut zone, ...)`, which Rust correctly rejected with E0502. Both paths now clone the selected mode into an owned `String` before the mutable ownership refresh and create the `&str` view only afterwards.
The affected paths are the live thermostat engine loop and `/api/control-plan` construction. No persisted schema, API field, ownership rule, thermostat decision, lockout rule or Home Assistant entity semantics changed.
### Validation available in this environment
- Confirmed both E0502 source patterns are removed and no `zone.mode.as_str()` borrow is held across `refresh_control_ownership(&mut zone, ...)`.
- `node --check` passed for Web JavaScript.
- All JSON files parse successfully.
- Python Home Assistant integration and helper scripts pass `py_compile`.
- Shell scripts pass `bash -n`.
- Version consistency checked for Cargo package, Cargo lock root package, README and Home Assistant manifest.
- This packaging container still has no `cargo`, `rustc` or `rustfmt`; therefore the target-host update script remains the authoritative Rust compile/test gate and must run `cargo test --all-targets` and `cargo build --release` before replacing the service.
---
# GREE Controller v0.8.0 - build and validation report
## 0.8.0 ownership/concurrency/safety release
Version 0.8.0 keeps the 0.7.13 public behavior while adding explicit control ownership, per-zone serialization and optimistic revisions, stale Home Assistant sensor protection, schedule-boundary recalculation for physical manual takeover, automatic Heat/Cool compressor lockout, desired-vs-actual diagnostics, and owner/source status in Web/Home Assistant.
### Compatibility
- Existing zone JSON remains loadable because every new persisted field uses serde defaults.
- The `revision` request field is optional, so older Web/API clients continue to work; clients that send it receive HTTP 409 on stale writes.
- Existing `control-plan.control_source` keeps its historical meaning (temperature source). The new command provenance is exported as `control_command_source` to avoid breaking Home Assistant or third-party consumers.
- Existing physical and zone climate entity unique IDs remain unchanged.
### Validation available in this environment
- `node --check` passed for Web JavaScript.
- All JSON files parse successfully.
- Python Home Assistant integration and helper scripts pass `py_compile`.
- Shell scripts pass `bash -n`.
- Structural checks confirm all `Zone` and `ZoneControlPlan` literals contain the newly required fields.
- A Rust compiler/toolchain is not installed in this container and external network access from the container is disabled, so `cargo fmt/check/test` cannot be executed here. The release/update scripts must still run the normal Cargo build/test gate on the target host before replacing the service binary.
---
# GREE Controller v0.7.13 - build and validation report
## Scope
Version 0.7.13 hardens Quick Thermostat ownership against polling/firmware-settling races, disables Global when no global Heating/Cooling mode is selected, and adds live counters for UDP frames received from configured GREE units. The 0.7.12 timed hand-back behavior remains unchanged.
### 0.7.13 race protection and diagnostics
- Per-zone Quick Thermostat changes are serialized with polling using the same per-device operation lock, preventing a stale poll from restoring a false physical/pilot takeover after the user explicitly selects local thermostat control.
- Recent controller-requested states and their pre-command baselines are kept for a bounded settling window, so delayed/out-of-order GREE status frames are not mistaken for remote-control changes.
- The Quick Thermostats **Global** button is disabled while the global house mode is neither Heating nor Cooling.
- Settings shows live total and per-device UDP receive counters, updated over the existing WebSocket event stream.
### Existing 0.7.12 hand-back correction
- Local thermostat ON/OFF now goes through one backend helper, so every new OFF action creates a fresh authoritative 15-minute deadline and ON always cancels it.
- A physical/direct-device takeover temporarily suspends expiry of an existing local-OFF hand-back. Returning the unit to the previous OFF state re-arms the countdown from that return moment instead of continuing an old timer.
- The Home Assistant coordinator keeps the exact `local_thermostat_resume_at` returned by the zone-control API in its short optimistic overlay, preventing a stale in-flight plan refresh from briefly restoring an older countdown.
## Implemented changes
- Night-mode settings moved out of application Settings into a dedicated **Night mode** page.
- Home Assistant connection, sensor aliases and integration access tokens moved to a dedicated **Home Assistant / Sensors** page.
- Settings now contains application/technical settings only.
- Unit-feature controls on device cards are smaller, centered and visually aligned with the other compact device controls.
- Disabled thermostat zones continue refreshing and showing room temperature while control commands remain disabled.
- Home Assistant sensor aliases are now presentation-only end-to-end: backend reads resolve aliases back to the original `entity_id`, settings are canonicalized, and zone/outdoor polling records the real entity ID.
- Outdoor temperature now falls back to the median outdoor reading from online GREE units when the configured Home Assistant outdoor sensor is unavailable.
- GREE optional capabilities are discovered from status properties (`Lig`, `Quiet`, `Tur`, `Air`, `Blo`, `Health`, `SwhSlp`) and persisted with each device.
- Supported unit features are shown in the Web UI and exposed as Home Assistant switches for panel light, Quiet, Turbo, X-FAN, Air, Health and Sleep.
- Night mode can use native GREE Sleep in the same command as the standby setpoint, fan limit and Quiet when the unit supports it.
- Home Assistant now exposes every controller zone as a full `climate` entity with Off / Auto (follow house mode) / Cool / Heat plus current and target temperature. Existing zone number/switch entities are retained.
- Decorative icons were removed from Settings block headers.
- Replaced the textual Connected/Disconnected label with a green/amber/red status dot in the top-left brand header.
- Fixed History filter layout so Zone/Device/Sensor and time-range controls keep equal heights and do not stretch after changing tabs.
- Reworked the Simulator into a Node-RED-style dotted flow board with connected live nodes for sensor input, thermostat target, decision, unit command and next event.
- Added configurable night mode (default 22:00-06:00): thermostat fan speed is capped during quiet hours and compatible units can be asked to keep Quiet enabled.
- Device cards now use **Name / protocol** instead of Rename and no longer show the fixed UDP port next to the IP address.
- Settings are split into visual blocks; the self-signed HA example uses a generic private address instead of an installation-specific address.
- Event/debug log retention is configurable and old rows are pruned automatically; the Events page can change retention and prune immediately.
- Home Assistant temperature sensors can have UI aliases; aliases replace raw entity IDs in history selectors, legends and custom-chart labels without changing stored entity IDs.
- Added a dedicated Web UI simulator page that visualizes thermostat/automation decisions as graphical blocks with hours, targets, demand, standby behavior, rule actions and upcoming events.
- Quick Thermostat cards on the dashboard can enable/disable a zone directly; the edit dialog is no longer required for this action.
- SQLite history housekeeping is tiered to chart resolution: full recent data, 10-minute buckets after 24 hours and 30-minute buckets after 7 days.
- Optional long-term InfluxDB archive supports both InfluxDB 1.x (database/basic auth) and 2.x (org/bucket/token), from persisted settings or environment variables.
- New samples are written to SQLite and InfluxDB in parallel when the archive is enabled.
- Maintenance backfills compacted legacy SQLite samples older than the configured Influx handoff age. A local row is deleted only after the Influx batch succeeds; archive failures keep the SQLite copy.
- History requests crossing the handoff age merge old InfluxDB data with recent SQLite data and fall back to SQLite when the archive query fails.
- Device commands are reduced to fields that actually changed. Optional GREE buzzer suppression uses `Buzzer_ON_OFF`/`BuzzerCtrl`; incompatible devices automatically fall back to normal frames and are remembered for the running process.
- Smart Fan now uses Low speed (`WdSpd=1`) when a zone is satisfied. The thermostat sends standby setpoint + Low fan + optional `Quiet=1` in one GREE frame. If a unit rejects `Quiet`, the controller retries without it and remembers that capability fallback until restart; when demand resumes, controller-owned Quiet is released on the transition.
- Toast progress indicators have horizontal inset so they stay inside rounded toast corners.
- Settings can be exported/imported as JSON. Metrics, event logs and generated API-token records are preserved during import. Exported configuration contains configured secrets and must be protected.
- Optional on-screen debug overlay is available on every Web UI route, can be changed through `/api/debug`, shows controller events plus live HTTP method/path/status timing and can include sanitized decrypted GREE request/response payloads.
- `/api/control-plan` exposes house state, current zone decisions, schedules, upcoming transitions and automation rules/time events.
- The dashboard renders the control plan as blocks with current targets/demand and upcoming actions.
- Home Assistant integration now also exposes whole-house/per-zone plan sensors, writable per-zone target-temperature `number` entities and per-zone enable `switch` entities via the restricted integration API.
## 0.6.1 compile hotfix
- Fixed `u8`/`u32` mismatches in communication-failure threshold handling without changing the persisted `Device.communication_failures` type or notification-settings schema.
- Removed the unused `AppState` parameter from Slack/Discord webhook delivery.
- Bumped application, Cargo lock root package, Home Assistant custom-component and PWA cache versions to `0.6.1`.
## Validation performed in packaging environment
- `node --check web/app.js`.
- Python syntax validation for the Home Assistant integration and helper scripts.
- Shell syntax validation for all `scripts/*.sh` files.
- JSON validation for EN/PL language packs, PWA manifest, Home Assistant manifest and HA translations.
- EN/PL UI translation-key parity and literal UI translation-reference validation.
- SQLite schema execution plus tiered compaction and event-log retention tests.
- SQLite archive-selection query tests for device, zone and Home Assistant histories.
- Configuration-clear behavior verified to preserve metric history, event logs and API tokens.
- Version consistency checked for Cargo package, Cargo lock root package, README and Home Assistant manifest.
- Archive ZIP integrity and SHA-256 manifest are verified during final packaging.
## Rust compiler note
The packaging environment does not contain `rustc`, `cargo` or `rustfmt`, so a Rust compile cannot be executed here. Rust unit tests cover unchanged-command filtering, history compaction, HA alias resolution, GREE outdoor fallback, night/native-Sleep decisions and combined standby command payloads; they cannot be executed in this packaging environment. The target LXC update/install scripts remain the authoritative compile gate and run:
```bash
cargo test --all-targets
cargo build --release
```
The update script must complete those commands before replacing the running binary.
## 2026-08-24 update
Implemented from the current source archive:
- Decimal thermostat input accepts both `.` and `,` in mobile/desktop UI via locale-independent parser and decimal keyboard hints.
- Notifications persisted in runtime settings with Pushover, Slack and Discord providers, test endpoint/button, two modes (`problems`, `important`), per-event cooldown, communication failure threshold and target-not-reached timeout.
- Device polling emits one offline anomaly after the configured consecutive-failure threshold and one recovery event when communication returns.
- Zone control tracks continuous demand and emits a `zone.target_timeout` anomaly once per demand episode when the target is not reached within the configured time.
- Automation dialog includes practical presets for high-temperature cooling, low-temperature heating, morning comfort and night power-off.
- Event log page has level/category filters and stronger visual separation by category/severity.
- Zone editor can copy thermostat tuning/profile settings from another zone without overwriting the destination zone identity, device assignment or HA entity.
- Language selector shows flags; theme selector shows compact theme icons.
- Home Assistant custom component includes `icon.png` and `logo.png` based on the existing project mark.
- New installs create a minimal environment file, keep `SIMULATE=false` and `AUTO_SEED=false`, and intentionally allow an empty application token. Existing env files remain untouched.
- Reverse-proxy support includes base-path routing, `X-Forwarded-Prefix`, prefix-aware UI/API/WebSocket/PWA assets and helper script health URLs.
- Removed permissive CORS and added basic browser security headers. Notification webhooks are HTTPS-only, host-restricted and do not follow redirects.
- Added `docs/SECURITY.md` and `docs/REVERSE_PROXY.md`.
History review: the supplied counts (28,133 GREE, 1,889 zone, 5,626 HA samples) are modest for the existing SQLite design. The current schema already has time/entity indexes, history bucketing, query limits, retention and compaction, so no database migration was added solely for these counts.
Validation performed in the build environment:
- `node --check web/app.js` - passed.
- JSON parsing of language files and web manifest - passed.
- `bash -n scripts/install.sh` - passed.
- Home Assistant icon files generated and verified as 256x256 RGBA PNG.
- Rust compilation was not executed because this execution environment does not contain `cargo`/Rust toolchain. Backend changes were therefore statically reviewed but require the normal project CI/build host to run `cargo test`/`cargo build --release` before deployment.
## 0.6.2 thermostat copy hotfix
- The destination zone identity is now stored independently from the form payload while editing.
- Copy-source choices exclude the currently edited zone.
- Copying thermostat settings explicitly preserves the destination zone ID, name, assigned device, sensor strategy/entity and enabled state.
- Zone updates use the independently tracked destination ID, preventing the source profile from being overwritten by a stale or mutated form ID.
## 0.6.3 house climate mode highlight
- The selected whole-house climate mode (Cool / Heat / Off) now has a persistent illuminated border, accent fill and glowing status dot.
- The active mode is still derived from persisted `settings.house_mode`, so the highlight survives refreshes and reconnects.
- Mode buttons now expose `aria-pressed` for an explicit accessible selected state.
## 0.6.4 Home Assistant zone thermostat naming
- Zone climate entities now use a descriptive entity name, `Thermostat`.
- New zone entities are therefore created as `climate.<zone_name>_thermostat` instead of falling back to ambiguous numeric suffixes such as `_2`.
- Existing zone climate registry entries are migrated on integration reload to the same scheme based on the zone's current name, fixing stale IDs left after zone renames (for example `climate.jan_2` for a zone currently named Igor).
- Migration is collision-safe: if the desired `climate.<zone_name>_thermostat` ID is already occupied, the existing entity ID is retained and a warning is logged instead of overwriting another entity.
## 0.6.5 house control, Home Assistant and LAN response time
- Whole-house thermostat `off` is presented as **Do not control** and no longer continuously powers units down. Directly operated devices remain untouched, while a zone explicitly overridden to Heat/Cool can continue controlling its own unit.
- Added a separate persistent whole-house master-power state. **Turn all off** powers enabled units down and prevents zone/controller automations from restarting them without modifying the selected thermostat mode/profile; **Turn all on** re-enables master power and powers enabled units on.
- Whole-house mode and preset buttons use the same normal active fill as other controls; the obsolete activity-dot styling was removed. Uniform whole-house presets remain visibly selected, while mixed zone profiles show no false global selection.
- Home Assistant now exposes controller-level **Thermostat mode** and **Work profile** select entities plus an **All air conditioners** master-power switch, backed by dedicated restricted HA endpoints.
- Device state stores the latest successful GREE communication round-trip time. **LAN network -> Devices** displays it in milliseconds next to the IP address and protocol.
- PWA cache key advanced to `gree-controller-v065`.
## 0.6.6 synchronized whole-house activation
- The global power panel is displayed above **House climate mode**.
- Selecting whole-house Cooling or Heating clears a previous global power-off and powers on all enabled units.
- Selecting Auto, Comfort, Sleep or Away as a whole-house profile does the same.
- House mode `off` / **Do not control** remains non-invasive and does not alter master power.
- The behavior is implemented in the shared backend endpoints, so Home Assistant house-mode/profile controls follow the same rules.
## 0.6.6 climate groups, HA off-target and standalone simulation
- Added persistent named climate groups based on thermostat zones, with independent group power, Heat/Cool/whole-house-follow mode and Auto/Comfort/Sleep/Away profile control.
- Added group controls directly below the global **All air conditioners** panel plus a dedicated **Groups** view for membership management.
- Whole-house **Turn all on**, Cooling/Heating and whole-house profile activation now re-enable all groups as part of their existing "all units" semantics; global power-off remains the authoritative master block.
- Automation actions can target either a direct device or a climate group and can apply group mode/power/profile changes.
- The control plan keeps a resolved thermostat target temperature while a zone, group or whole-house inherited mode is off, so Home Assistant zone climate entities no longer expose the configured target as `unknown` solely because control is disabled.
- Home Assistant documentation and zone attributes clarify that zone thermostat **Auto** means follow the controller's whole-house Heating/Cooling mode; the direct physical-device climate entity still uses native GREE Auto.
- The simulator can be opened as a standalone new-tab view, switched to browser full-screen mode, and filtered by individual unit or climate group.
- Configuration backup/import, bootstrap/WebSocket state, SQLite schema and API documentation include groups.
Validation performed in this packaging environment:
- `node --check web/app.js` and `node --check web/sw.js`.
- Home Assistant Python modules compiled with `python3 -m py_compile`.
- English/Polish translation packs parsed and checked for key parity and static UI references.
- HTML IDs and translation references checked for duplicates/missing keys.
- `INIT_SCHEMA` executed against an empty SQLite database and the new `climate_groups` table verified.
- Rust compilation cannot be executed here because the environment does not contain `cargo`, `rustc` or `rustfmt`; the backend changes were statically reviewed and still require the normal LXC/CI `cargo test --all-targets` and `cargo build --release` gate before deployment.
## 0.6.7 dashboard hierarchy
- Reorganized the main dashboard into three levels: whole-house controls, climate-group controls, and secondary details.
- Global master power and whole-house climate mode/profile now share one panel instead of competing for separate dashboard space.
- Group controls remain directly below global control, use a compact horizontal strip, and omit duplicate mode/profile/member summary boxes on the dashboard while retaining full details on the Groups page.
- Control plan, quick thermostats and direct device controls remain available from the dashboard but are collapsed by default and can be expanded independently.
- Dashboard section badges show current group, zone and device counts.
- Mobile layout keeps global actions and expandable sections compact without removing any controls.
- PWA cache key advanced to `gree-controller-v067-dashboard` so the reorganized web assets replace cached v0.6.6 files after update.
## 0.6.8 compact dashboard without horizontal scrolling
- Removed the horizontally scrolling group strip from the main dashboard.
- Dashboard group cards now use a responsive wrapped grid and flow into additional rows.
- Group mode/profile controls use fixed grids on the dashboard, so they never require horizontal scrolling.
- Whole-house profile controls also wrap into a grid on narrow screens.
- Kept Control plan, Zones and Devices as collapsed disclosure panels.
- Reduced dashboard hero, metric, global-control, group-card and disclosure spacing for a denser layout without removing functionality.
- Advanced PWA cache key to `gree-controller-v068-dashboard-wrap`.
## 0.6.9 dashboard fit, compact summary and group-aware control plan
- Reworked dashboard Quick Thermostats so the target-temperature control occupies its own row and measured/device-target temperatures sit below it; labels no longer collide or clip in medium-width cards.
- Kept the quick-thermostat list as a wrapped responsive grid with no horizontal scrolling.
- Compressed the Home / Comfort under control summary into one short card; average temperature and small online/active/demand counters now share the same row instead of consuming a separate large hero plus metrics row.
- Reduced dashboard control-plan blocks to compact simulator-style cards.
- Added explicit climate-group cards to What happens next, combining member demand and upcoming member-zone events.
- Zone plan cards identify their climate group membership, and automation plan rows preserve group targets.
- Advanced the PWA cache key to `gree-controller-v069-dashboard-fit`.
## 0.7.0 UI cleanup and scheduler/automation integrity audit
- Removed directional arrow glyphs from Swing controls on dashboard and device cards.
- Removed the divider above the unit feature block and tightened desktop quick-mode/fan controls so all buttons fit without horizontal scrolling.
- Allowed the detailed zone quick-change hint to wrap naturally on desktop.
- Added an explicit Mode policy hint: keep Follow house mode when the zone should inherit the global GREE Controller mode; fixed Cooling/Heating are intentional overrides.
- Fixed the Always-comfort schedule template to represent a true 24-hour window without a one-minute gap.
- Added overlap detection for enabled schedules, including overnight windows, and deterministic handling for legacy overlapping rows.
- Fixed the Workday template Sunday-to-Monday handoff so Sunday Sleep ends at 06:30 before Monday Morning starts.
- Manual zone/profile overrides now expire at the actual next weekly schedule transition; if none exists, they remain active until explicitly cleared instead of expiring after an arbitrary fallback timeout.
- Editing/deleting/applying schedule templates refreshes the active manual-override boundary.
- Time automations are limited to one execution per local minute even with the minimum 30-second cooldown; failed actions now also observe cooldown to avoid retry/log storms.
- Temperature automations ignore stale readings from disabled/offline trigger devices.
- Direct-device automations cannot wake a unit blocked by a disabled climate group.
- Group power-on respects other disabled overlapping groups; group power-off remains authoritative.
- Automation creation/import validates target/trigger references, empty actions and unsupported group-action fields.
- Device/zone deletion now protects automation/group references and removes orphaned schedules. A physical device can be assigned to only one thermostat zone.
- Control-plan event timestamps are normalized to minute boundaries; time-event preview covers DST-length local days.
- Added Rust unit-test cases for overnight/full-day schedules, overlap detection, weekly boundary lookup, Workday weekend handoff and one-fire-per-minute time automations.
Rust compilation/tests still require a host with the Rust toolchain (`cargo`/`rustc`); this build environment does not provide it. Static frontend, JSON, Python/Home Assistant, archive and manifest validation are performed below.
Validation for the 0.7.0 package:
- `node --check` passed for `web/app.js` and `web/sw.js`.
- PL/EN JSON and Home Assistant manifest parsed successfully; PL/EN translation key sets are identical (578 keys each).
- Home Assistant Python modules passed `python3 -m py_compile`.
- `web/index.html` parsed with Python's HTML parser; CSS braces are balanced.
- No Swing arrow glyph remains in web controls, the device feature divider rule is removed, and the detailed-zone quick hint has an explicit wrapping rule.
- All built-in schedule templates were exercised against the weekly minute-mask model: no overlaps; Family/Child/Bedroom/Always cover the full week, and Workday's intentional uncovered period falls back to the normal Comfort profile.
- SQLite `INIT_SCHEMA` and device-delete orphan-schedule cleanup SQL were executed successfully against temporary in-memory databases.
- Rust source delimiter/static checks passed. Rust compile/test execution is unavailable here because no Rust toolchain is installed.
## 0.7.1 quick-control visual consistency
- Refactored dashboard-only thermostat/device controls to semantic `quick-control-card` and `quick-control-row-*` classes.
- Quick thermostat preset/mode controls and quick manual HVAC/fan/function controls now share the same compact height, spacing, typography and fixed responsive grids.
- Removed the superseded `#dashboardDevices` desktop-only size override; full Devices/Zones views remain unchanged.
- Changed the compact inherited-mode label from `Dom/House` to `Globalny/Global`, while retaining the internal `house` value for API/backward compatibility.
- Changed `Sen teraz/Sleep now` to `Sen/Sleep`.
Validation for the 0.7.1 package:
- `node --check` passed for `web/app.js` and `web/sw.js`.
- PL/EN JSON parsed successfully and translation key sets are identical (574 keys each).
- Home Assistant manifest parsed successfully and Home Assistant Python modules passed `python3 -m py_compile`.
- `web/index.html` parsed with Python's HTML parser and CSS brace balance passed.
- Static assertions confirm the compact classes are attached only to dashboard quick cards (`detailed == false`), while full Zone/Device cards keep their existing classes and sizes.
- Quick controls use explicit 3/4/5-column grids with no horizontal overflow behavior; all compact option buttons share the same 28 px minimum height, 9 px typography and 4 px gap.
- User-facing inherited-mode labels are `Globalny/Global`; the internal/API value remains `house`, preserving backward compatibility.
- No thermostat, schedule, automation, group, database or device-control logic was changed in 0.7.1.
## 0.7.2 zone configuration separation
- Reworked the Zones list into read-only status + configuration cards.
- Removed live temperature, preset, mode and enable/disable controls from the Zones list.
- Added navigation from a zone configuration card to its matching Dashboard quick thermostat.
- Kept backend zone-control endpoints and thermostat logic unchanged.
- Kept the legacy `setpoint` field in form submission as a hidden compatibility value while profile temperatures remain the explicit configuration surface.
## 0.7.2 packaging hotfix
- Corrected an invalid `Cargo.lock` entry that labeled the `tokio-util` package as `0.7.29` while retaining the checksum and dependency resolution for `0.7.19`.
- Restored `tokio-util = 0.7.19`, which satisfies `tower-http 0.6.11` (`^0.7`) and is present in the crates.io index used by the target LXC.
- No application source, database schema, scheduler, automation, device-control or Web UI logic changed in this hotfix.
## 0.7.3 Home Assistant climate-group support
- Added restricted HA group discovery and group-control endpoints.
- Added one Home Assistant device per configured climate group.
- Added group Power switch, Thermostat mode select, Work profile select and Control plan sensor.
- Group reporting derives common mode/profile from member-zone configuration and reports mixed states without coercing them.
- Group plan attributes include members, online unit count, average current temperature, demand count and upcoming zone schedule events.
- Existing group-control engine remains the single command path, including global-master-power and overlapping-disabled-group protections.
### 0.7.3 validation
- `python3 -m py_compile home-assistant/custom_components/gree_controller/*.py` passed.
- Home Assistant manifest and PL/EN translation JSON parsed successfully.
- `Cargo.toml` parses successfully and both Cargo package metadata and HA manifest report `0.7.3`.
- `Cargo.lock` keeps the packaging hotfix at `tokio-util = 0.7.19`.
- Static assertions confirm the restricted HA router exposes group discovery/control, the API client polls groups, and select/switch/sensor group entities are registered.
- `node --check web/app.js` and `node --check web/sw.js` passed.
- `bash -n scripts/*.sh` passed.
- HTML parsing and CSS brace-balance checks passed.
- The validation environment does not provide `cargo`/`rustc`, so the Rust test/build step must run on the target host/LXC (the normal `scripts/update.sh` performs `cargo test --all-targets` before installation).
## 2026-08-25 disabled-zone and standby-command hotfix
- Disabled thermostat zones are excluded from normal house, group-loop and direct-device automation ownership while sensor polling/history continue normally.
- Disabling an active zone now sends one immediate power-off command to its assigned unit. Later manual operation remains possible through the technical Devices control path.
- Whole-house ON only powers devices assigned to enabled thermostat zones; unassigned devices and devices belonging to disabled zones are not started.
- Whole-house OFF now cascades group power gates to OFF and powers off all enabled physical devices. Whole-house ON restores group gates but still respects disabled zones.
- Restricted Home Assistant direct-device commands are rejected for devices assigned to disabled zones; zone/group/house endpoints keep their managed semantics.
- Dashboard direct-device controls are locked for devices assigned to disabled zones and point the operator to the Devices view for explicit technical/manual control.
- Smart Quiet now follows demand transitions instead of being reasserted throughout steady standby. Outside the night window it no longer fights Smart Fan by toggling Quiet off/on on successive cycles.
- Standby Low fan is sent on a relevant transition/core thermostat change rather than retried forever when firmware reports a different fan state, preventing periodic command/beep loops with unchanged room temperature and setpoint.
- Added regression assertions for steady-standby Smart Quiet behavior and night-mode/Smart-Fan interaction.
- PWA cache key changed so browsers fetch the corrected frontend and language strings after deployment.
Validation in this packaging environment:
- `node --check web/app.js` and `node --check web/sw.js` passed.
- PL/EN application language JSON parsed successfully and translation-key parity passed.
- `web/index.html` parsed successfully with Python's HTML parser.
- Home Assistant Python modules and JSON manifests/translations passed syntax/parse checks.
- Shell scripts passed `bash -n`.
- Rust source was statically reviewed and delimiter-checked. This environment does not provide `cargo`, `rustc` or `rustfmt`; the normal target update path must run `cargo test --all-targets` and `cargo build --release` before installation.
## 0.7.4 physical/manual takeover
- Added persistent per-zone device manual override state with source fields, start time and optional next-schedule expiry.
- Polling detects external power/mode/setpoint/fan changes relative to the last controller-known state and suspends automatic correction for that zone.
- Known standby Low-to-Auto fan normalization is ignored to avoid false remote-control detection.
- Technical climate commands from the Devices endpoint enter the same takeover; light-only/non-thermostat feature changes do not.
- Manual takeover has priority over zone modulation, climate groups and direct-device automations. Whole-house power-off is the explicit exception and clears all takeovers.
- Added Web UI Manual control / remote state and Resume automation action, plus control-plan fields for integrations/simulation.
- Added regression tests for external climate-state detection, Low-to-Auto suppression and takeover reset state.
### 0.7.4 validation
- `node --check` passed for `web/app.js` and `web/sw.js`.
- PL/EN language packs parse successfully, have identical key sets (586 keys each), and all UI translation references resolve.
- Home Assistant Python modules and helper Python scripts pass `py_compile`; shell scripts pass `bash -n`.
- Cargo metadata parses from `Cargo.toml`; package, Cargo lock root package and Home Assistant manifest report `0.7.4`.
- `web/index.html` parses successfully and CSS braces are balanced.
- Rust source delimiter/static invariant checks pass, including poll detection, group/automation guards, whole-house override clear and technical manual-command routing.
- The packaging environment has no `cargo`, `rustc` or `rustfmt`; the target update path remains the compile gate and must run `cargo test --all-targets` and `cargo build --release` before replacing the running binary.
## 0.7.5 UI polish
- Added explicit active styling for climate-group **ON/OFF** buttons, matching the existing active mode/profile treatment.
- Reworded simulator demand summary to **Zones with demand** / **Strefy z zapotrzebowaniem**.
- Bumped application and Home Assistant integration version to `0.7.5` and rotated the PWA service-worker cache key.
- Validation: `node --check` passed for the web scripts, PL/EN language packs parse with identical 587-key sets, version declarations are consistent and the package SHA-256 manifest verifies.
- `cargo check` remains unavailable in this environment because the Rust toolchain is not installed.
## 2026-08-26 manual takeover, per-unit quick power and restart safety
Implemented against the supplied v0.7.5 source archive:
- Manual/remote takeover now stores the pre-takeover physical climate state in the persisted zone payload.
- If the user restores that operational state, the takeover clears automatically. When the previous state was OFF, returning to OFF is sufficient even if the remote retained a different dormant target/mode, so a stale **Resume automation** prompt is not shown.
- If the manual state is not restored, the existing next-schedule-boundary expiry remains unchanged; zones without a future transition still require an explicit resume.
- Dashboard **Quick thermostats** now include per-unit ON/OFF controls. They intentionally use manual/remote semantics, affect only that physical unit and do not enable its climate group.
- Cooling controlled by a valid external/combined room sensor now applies a small pre-rounding demand bias so the unit selects the next lower whole-degree target (normally 0.5-1.0 C below the room target). The bias is disabled during GREE fallback/discrepancy fallback and is not added on top of a larger outdoor-assist correction.
- Startup thermostat/schedule/automation command emission is gated until one complete poll attempt of all enabled devices finishes. This prevents stale persisted device state from producing a restart-only command while leaving manual API control available.
- `/api/health`, bootstrap system data and `/api/system/info` expose `control_ready` for startup diagnostics.
- Advanced the PWA cache key to `gree-controller-v075-zone-manual-power-restart` so changed dashboard assets are refreshed.
Validation performed in this packaging environment:
- `node --check web/app.js` - passed.
- `node --check web/sw.js` - passed.
- EN/PL JSON parsing and translation-key parity - passed (590/590 keys).
- Static `tr(...)`/`data-i18n` references in the Web UI - all resolved.
- `web/manifest.webmanifest` JSON parsing - passed.
- `bash -n scripts/*.sh` - passed.
- Home Assistant Python modules and helper Python scripts compiled with `python3 -m py_compile` - passed.
- All direct `Zone { ... }` constructors were updated for the new persisted baseline field.
Rust compiler note: this packaging environment still does not provide `cargo`, `rustc` or `rustfmt`, so the Rust unit tests and release build cannot be executed here. The normal deployment/update compile gate must still run `cargo test --all-targets` and `cargo build --release` before replacing the running binary.
## 2026-08-26 Home Assistant state-bounce fix
- Added bounded post-command GREE status settling retries (0/150/350/650 ms) so an acknowledged write is not immediately contradicted by a firmware status frame that still contains the pre-command state.
- Added a three-second Home Assistant pending-command guard for physical-unit climate and optional-feature switches. Fresh command intent survives overlapping/stale coordinator refreshes, then automatically yields to factual controller polling.
- Kept the guard intentionally short and non-persistent; it does not change restart behavior or permanently mask a failed command.
## 2026-08-26 v0.7.7 quick-control desktop label fit
- Reproduced the dashboard quick-device layout issue from the supplied screenshot: five equal columns left too little usable width after button padding for Polish `Chłodzenie`/`Osuszanie`.
- Rebalanced only the five quick-device mode columns and reduced their horizontal padding from 5 px to 3 px. This keeps all five labels on one line without reducing the shared 9 px control font.
- Verified the Home Assistant integration already exposes every controller zone as an independent climate thermostat with `TURN_ON`/`TURN_OFF`, plus a separate per-zone Enabled switch. Turning on a zone thermostat sends only `enabled: true` for that zone; it does not power the whole group. Physical indoor units also remain independently controllable through their own climate entities.
- Bumped application/Home Assistant package metadata to `0.7.7` and rotated the PWA cache key.
## 2026-08-26 v0.7.8 zone status wording and quick unit power
- Replaced the misleading thermostat runtime labels **Potrzeba komfortu** / **Komfort / standby inwertera** with **Zapotrzebowanie aktywne** / **Cel osiągnięty**. The Comfort wording remains reserved for the actual Comfort preset/profile. Runtime status also reports **Oczekiwanie** when thermostat control is paused by mode and **Brak pomiaru** when no valid control temperature is available, so **Cel osiągnięty** is not used as a generic idle state.
- Simplified per-unit manual power in Dashboard -> Quick thermostats: removed the redundant **Jednostka · pilot** caption and the separate ON/OFF pair. A single contextual **Włącz jednostkę** / **Wyłącz jednostkę** button now directly controls only that physical unit with the existing manual-takeover semantics.
- Bumped package/Home Assistant metadata to `0.7.8` and rotated the PWA cache key.
## 2026-08-27 v0.7.9 zone profile and Home Assistant fixes
- Explicit quick-zone target/mode/profile/enable actions now refresh the runtime profile target immediately and wake the regulator instead of waiting for the next fixed thermostat interval.
- Manual/remote takeover no longer exposes a physical standby target (for example 25 C) as the zone thermostat target. The thermostat profile target and physical `device_setpoint` are reported separately.
- Control-plan `enabled` is now the configured per-zone switch; new `effective_enabled` reports disabled-group gating without making the Home Assistant zone switch bounce to Off.
- Home Assistant zone commands have a short pending-state guard against stale overlapping refreshes.
- Zone climate entities support Auto/Comfort/Sleep/Away presets and each zone device has a dedicated **Work profile** select.
- Added a focused Rust unit test for immediate manual-profile target refresh.
Validation for this package is recorded after the final archive/manifest checks. Rust compile/test still requires the normal target/CI host because this packaging environment has no Rust toolchain.
## 2026-08-27 v0.7.10 local quick thermostat and false takeover fix
- Added persisted `local_thermostat_power` ownership with three distinct states: normal group/schedule ownership (`null`), locally forced thermostat ON (`true`) and locally forced thermostat OFF (`false`).
- Dashboard quick ON/OFF uses local thermostat semantics and never calls the direct/manual-device command path.
- A locally active thermostat bypasses disabled climate-group power gates while retaining the normal sensor, hysteresis, profile/target, Smart Fan and GREE setpoint-modulation logic.
- **Resume automation** for a local thermostat clears local ownership and returns the zone to group/schedule control. The existing device takeover Resume action remains separate.
- Direct device commands and genuine remote/pilot changes still create `device_manual_override`; local thermostat ownership does not.
- Added a bounded pending controller-command record for GREE climate fields when post-command status has not settled. A later poll matching that expected state is no longer misclassified as remote control, fixing false **Manual control / remote** after group ON and similar controller-originated transitions.
- Direct device automations are suppressed while local thermostat ownership is active so they cannot fight the local regulator.
- Home Assistant zone climate ON/OFF now maps to local thermostat power; the separate Enabled switch remains the technical zone enable.
- Rotated the PWA cache key and advanced package/Home Assistant metadata to `0.7.10`.
## Final validation for v0.7.10
- `node --check` passed for all Web UI JavaScript files.
- Home Assistant modules, helper scripts and `make_zip.py` passed `python3 -m py_compile`.
- All JSON files parsed successfully; Polish and English language packs have identical 598-key sets.
- All shell scripts passed `bash -n`; `web/index.html` parsed successfully and CSS braces are balanced.
- Cargo package, Home Assistant manifest, README and PWA cache metadata consistently report version `0.7.10`.
- Rust source delimiter/static invariant checks passed, including the new regulator wake-up and control-plan fields.
- `FILE_MANIFEST.sha256` is regenerated from the final tree and verified before packaging; the resulting ZIP is tested with `unzip -t`.
- This environment does not provide `cargo`, `rustc` or `rustfmt`, so the normal target/CI update path must still run `cargo test --all-targets` and `cargo build --release` before replacing the running binary.
## 2026-08-27 v0.7.11 cleanup and compile fix
- Removed the obsolete `/api/zones/{id}/manual-power` compatibility route, request structure and handler. The only zone quick-power API is now `POST /api/zones/{id}/control` with `power:true/false`.
- Renamed Web UI local-thermostat power internals so they no longer use misleading `manual-power` terminology.
- Fixed Rust error E0505 in `local_thermostat_ownership_blocks_direct_automation`: the test now passes a borrowed one-element slice with `std::slice::from_ref(&zone)` instead of moving `zone` while borrowing `zone.device_id`.
- Advanced package/Home Assistant/PWA metadata to `0.7.11`.
## Final validation for v0.7.11
- `node --check` passed for Web UI JavaScript files.
- Home Assistant modules, helper scripts and `make_zip.py` passed `python3 -m py_compile`.
- JSON files parsed successfully; Polish and English language packs have identical 602-key sets.
- All shell scripts passed `bash -n`; `web/index.html` parsed successfully and CSS braces are balanced.
- Runtime source trees (`src`, `web`, `home-assistant`, `scripts`) contain no legacy `manual-power` route, handler, symbol or CSS naming.
- Cargo package and Home Assistant manifest report version `0.7.11`; PWA cache metadata was rotated for this release.
- The reported Rust E0505 test case was corrected with borrow-only `std::slice::from_ref(&zone)` calls.
- This packaging environment does not provide `cargo`, `rustc` or `rustfmt`; target/CI must run `cargo test --all-targets` and `cargo build --release` before deployment.
## 2026-08-27 v0.7.12 timed local-thermostat resume
- Added persisted `local_thermostat_resume_at` to each zone.
- Local Quick Thermostat ON has no timeout; local OFF powers the unit down immediately and schedules automatic automation hand-back after exactly 15 minutes.
- The control loop now sleeps only until the earlier of its normal interval or the nearest local-resume deadline, so the 15-minute hand-back is not delayed by a long zone interval and remains restart-safe.
- Expiry clears local power ownership and quick preset/setpoint overrides, then re-evaluates the current group, house mode, schedule, temperature and hysteresis. It never restores a stale physical ON/OFF snapshot.
- Physical/pilot `device_manual_override` is intentionally untouched by the local timer.
- Web UI displays a live `MM:SS` countdown and **Resume now** during the local-OFF window.
- Persisted 0.7.10/0.7.11 local-OFF records without a deadline receive a fresh 15-minute deadline on first control cycle after upgrade.
- Home Assistant zone attributes expose `local_thermostat_resume_at`.
- Added a unit test that verifies local hand-back clears local quick state without clearing a real physical-device takeover.
## Final validation for v0.7.12
- Web JavaScript passes `node --check`.
- Home Assistant modules and helper scripts pass `python3 -m py_compile`.
- JSON and TOML metadata parse successfully; Polish and English translation packs have identical key sets.
- Shell scripts pass `bash -n`; HTML parses and CSS braces are balanced.
- Source references for `local_thermostat_resume_at` are consistent across model, API, engine, Web UI, control-plan output and Home Assistant attributes.
- Version metadata is aligned at `0.7.12` and the PWA cache key is rotated.
- This packaging environment still has no `cargo`, `rustc` or `rustfmt`; target/CI must run `cargo test --all-targets` and `cargo build --release` before deployment.
## 2026-08-27 v0.7.13 quick-thermostat race protection and frame counters
- Quick Thermostat changes from Web/Home Assistant are serialized with the same per-device operation lock used by polling/manual-takeover detection.
- Controller-originated climate changes retain a bounded settling history of requested and pre-command states so delayed/out-of-order GREE status frames do not falsely trigger physical/pilot takeover.
- Dashboard Quick Thermostat **Global** is disabled whenever the global house mode is neither Heating nor Cooling.
- Settings exposes live total and per-device UDP receive counters for configured GREE units via the existing WebSocket stream.
- The Home Assistant zone `climate` entity remains the supported path for controlling one thermostat independently.
## Final validation for v0.7.13
- `node --check` passed for `web/app.js` and `web/sw.js`.
- All JSON files parsed successfully; Polish and English language packs have identical 610-key sets.
- Home Assistant Python modules and helper Python scripts pass `py_compile`; shell scripts pass `bash -n`.
- `web/index.html` parses successfully and CSS brace balance passes.
- Cargo package, Home Assistant manifest, README and PWA cache metadata are aligned at `0.7.13`.
- The final SHA-256 file manifest and ZIP integrity are verified during packaging.
- This packaging environment does not provide `cargo`, `rustc` or `rustfmt`; target/CI must run `cargo test --all-targets` and `cargo build --release` before deployment.