# GREE Controller project specification ## Product goal Build an autonomous local GREE HVAC controller running primarily as a Rust service in a dedicated Linux/LXC environment. Home Assistant is optional: it can provide external sensor data and can consume controller devices through a thin custom integration, but it must not contain the core GREE protocol or heating logic. ## Architecture ```text Home Assistant / Web UI / REST clients | REST + WebSocket | GREE Controller (Rust) +--------------------------------+ | Device/state manager | | GREE protocol UDP/AES | | Zone/heating engine | | Scheduler | | Automation engine | | SQLite | | Mobile-first web UI | +--------------------------------+ | UDP/7000 | GREE AC ``` ## Independence requirements - GREE control must work when Home Assistant is stopped. - Local schedules and automations must continue without HA. - HA sensor failures must never remove basic access to the AC. - Device state is persisted by the controller and can be restored to clients after reconnect/restart. ## GREE protocol layer The protocol implementation is isolated from application logic and covers: - discovery, - packet encoding/decoding, - AES-128-ECB support, - AES-128-GCM envelope support, - bind/key acquisition, - status polling, - command transport, - reconnect/offline handling. The rest of the application works with generic device state and commands rather than raw protocol packets. ## Device model Each device stores: - stable controller ID, - MAC/CID, - name, - IP/port, - protocol version, - model/firmware when available, - encryption key when bound, - enabled/simulated flags, - power and HVAC mode, - target/current/outdoor temperature, - fan, swing, quiet, turbo, light, - online/last-seen/error state. ## Smart thermostat, zones and schedules The primary user model is a fast thermostat, not raw device automation. A global climate mode selects the season (`cool`, `heat`, `off`). Zones follow it by default and can optionally override Heat/Cool. The UI labels this as **Mode policy**: keep **Follow global mode** when the zone should inherit the mode selected globally in GREE Controller; the fixed Cooling/Heating policies are explicit overrides. House `off` is a **do not control** state for inherited zones: manual device operation is left untouched, while a zone explicitly switched to Heat/Cool can operate independently. Whole-house power on/off is a separate master state. Master Off is authoritative for zones and controller automations but does not modify the selected house thermostat mode. During normal heating/cooling the controller minimizes unit power cycling. It keeps the indoor unit powered and uses **setpoint modulation**: - when the zone requires conditioning, use the active target (optionally assisted slightly by outdoor weather), - when the zone is satisfied, move the device setpoint to the non-demand side of the room target so the inverter/compressor can stop naturally, - use hysteresis and a minimum adjustment interval to avoid command chatter, - optionally adjust fan speed based on room error and outdoor extremes; with Smart fan enabled, a satisfied zone uses Low fan together with the standby setpoint and requests Quiet mode in the same frame when the unit supports it. Unsupported Quiet commands automatically fall back to Low fan without breaking thermostat control. Each zone has separate seasonal profile temperatures: - Cooling: Comfort / Sleep / Away, - Heating: Comfort / Sleep / Away. The zone can use the GREE internal sensor, its own Home Assistant room sensor, or a weighted combination. External room sensors are configured per zone; loss/discrepancy falls back safely to the GREE sensor. Weekly schedules reference profiles instead of duplicating temperatures. Ready-made Family, Child room, Bedroom, Workday and Always-comfort templates create ordinary schedule rows that remain fully editable. A manual **Sleep**, Comfort, Away or custom-temperature override ends automatically at the next schedule boundary; if no future transition exists, it remains active until explicitly cleared. Whole-house preset actions apply the same temporary policy to every zone. Automations remain available for advanced exceptions. Daily comfort should be implemented with zones/profiles/schedules so direct-device automation commands do not compete with the thermostat engine. Climate groups are named collections of thermostat zones (for example Upstairs/Downstairs). Group commands can independently set power, Heat/Cool/house-follow mode and Auto/Comfort/Sleep/Away profile for only their member zones. Automation actions may target either a direct device or a climate group. ### Outdoor temperature assist A configured Home Assistant outdoor-temperature entity is optional auxiliary context. It never replaces the room-control temperature. Under strong heat/cold the engine may slightly bias the active AC target and increase Smart Fan airflow. If HA is unavailable, the smart thermostat continues without outdoor assistance. ## Scheduler and automations Schedules are weekly time windows and may cross midnight. Equal start/end times mean a 24-hour block for each selected weekday. Enabled schedules for one zone may not overlap. Schedules can select Comfort/Sleep/Away or a custom setpoint. Automations support time or temperature triggers and device commands with cooldown protection; a failed command also observes the cooldown instead of retrying every control cycle. They are intended for exceptions rather than the normal daily thermostat cycle. The automation engine runs in Rust and does not require Home Assistant YAML automation logic. ## API The controller exposes REST for configuration/commands and WebSocket for live state updates. The HA integration and web UI use the same controller API. ## Home Assistant direction 1: external sensor input The Rust application can read a specifically configured HA entity using a Long-Lived Access Token. This input is optional and is not a prerequisite for GREE control. ## Home Assistant direction 2: native climate entities A custom integration creates HA climate entities and translates HA service calls to controller API commands. It does not communicate with GREE directly. For migrations from the default GREE integration, a mapping file can request an existing entity ID such as `climate.klima_salon`. The previous integration must release that ID before takeover. ## Web UI Primary use is from a phone. Requirements: - responsive mobile-first layout, - touch-friendly controls, - live state updates, - device, zone, schedule, automation, history and diagnostics screens, - JSON-based language packs from `lang/*.json`, with English as the required default/fallback and Polish included, - automatic language discovery at build time, so a new valid `.json` file adds a language without JavaScript changes, - language stored in the `gree_controller_language` cookie, - system/light/dark appearance modes, - appearance stored in a cookie, - flat visual design without decorative shadows, - no letter-logo badge next to the application name. ## Storage SQLite stores configuration, state, readings and events. PostgreSQL or other external database services are not required for the single-node deployment target. ## Deployment Primary target: ```text LXC / Debian or Ubuntu /opt/gree-controller/gree-controller /etc/gree-controller.env /var/lib/gree-controller/gree-controller.db systemd: gree-controller.service ``` A development script prepares dependencies/build/runtime configuration, and a separate installer creates the systemd deployment. ## Security - optional administrator Bearer token for the controller Web/API, - separately generated, revocable Home Assistant client tokens stored only as SHA-256 hashes, - Home Assistant client tokens are restricted to device read/control endpoints, - secrets kept outside source control, - no direct public Internet exposure, - TLS delegated to a trusted reverse proxy/VPN when required, - local device keys and HA token protected in environment/database files, - invalid/self-signed HA HTTPS certificates are rejected by default; bypass is an explicit trusted-LAN opt-in. ## Per-zone room sensors A zone represents one room and normally maps one GREE indoor unit to one optional external room-temperature sensor. External sensors are not global. For example, Living Room can use `sensor.living_room_temperature` while Bedroom independently uses `sensor.bedroom_temperature`. The zone controller supports `device`, `combined`, and `home_assistant` temperature strategies. Combined mode uses a configurable external-sensor weight (40% by default), validates the difference between sensors, and falls back to the GREE sensor when the external source is missing or outside the configured discrepancy limit. The local GREE measurement therefore remains available even when Home Assistant is offline. ## Operational packaging conventions - All operator-facing shell/Python utilities live under `scripts/`. - `scripts/install.sh` performs the first systemd/LXC installation. - `scripts/update.sh` performs an in-place update with a stopped SQLite backup, health check and automatic rollback. - `scripts/service.sh` provides common systemd operations. - Cargo's `build.rs` stays at the package root because Cargo requires that location. - Every SQLite statement and schema definition is centralized in `src/queries.rs`; database/domain code must not embed SQL strings elsewhere. ## Web UI design system (v0.4.4) The embedded UI uses the classic GREE Controller visual language: large rounded surfaces, circular thermostat controls, comfortable spacing, compact desktop navigation and mobile bottom navigation. Light, Dark and System themes use neutral surfaces with a restrained green accent. The UI ships as a normal static `web/styles.css` file embedded into the Rust binary. There is no frontend package manager, CDN, or CSS build pipeline. ### Rich zone history Zone history stores GREE temperature, optional Home Assistant room temperature, calculated control temperature, profile target, actual device setpoint, outdoor temperature assist, power, mode, fan speed, demand, active preset and control source. The UI can compare all zones on common temperature/target charts or inspect one zone in detail. ### History information architecture (v0.4.4) History is not a single long page. It is split into URL-addressable views: Overview, Zones, GREE devices, HA sensors and Custom chart. Device history exists independently from zones, HA sensors have their own history stream, and zone history adds the thermostat/control context. A compatibility fallback maps existing GREE device readings into a zone timeline when the richer zone table is still empty. The custom chart composer can mix GREE indoor/outdoor/target values, zone GREE/HA/control/target/device-setpoint/outdoor values, and HA temperature entities. Saved definitions persist in browser storage and a chart definition can be serialized into the `/history/custom?chart=...` URL for direct sharing/bookmarking. Quick thermostat temperature nudges are optimistic in the browser and stored separately from the selected Comfort/Sleep/Away/Auto preset. Changing `+/-` therefore does not silently leave Auto mode.