321 lines
15 KiB
Markdown
321 lines
15 KiB
Markdown
# GREE Controller
|
|
|
|
> Identifier convention: project-owned namespace identifiers use `gree_controller`. Home Assistant uses `gree_controller`, UI storage uses `gree_controller_*`, and environment variables use `GREE_CONTROLLER_*`.
|
|
|
|
Standalone local GREE air-conditioner controller written in Rust. It runs on a regular Linux host or an LXC container and provides a mobile-first web interface without depending on the vendor cloud.
|
|
|
|
Current version: **0.3.5**.
|
|
|
|
## Highlights
|
|
|
|
- local GREE discovery over UDP/7000,
|
|
- V1 AES-128-ECB and V2 AES-128-GCM discovery/bind/status/command transport,
|
|
- automatic ECB/GCM detection from discovery responses and bind fallback,
|
|
- power, HVAC mode, target temperature, fan, vertical/horizontal swing, quiet, turbo and display light,
|
|
- SQLite state/history/event storage,
|
|
- temperature zones with hysteresis and minimum ON/OFF protection,
|
|
- weekly schedules including ranges that cross midnight,
|
|
- temperature/time automations,
|
|
- per-zone optional Home Assistant room-temperature sensors with GREE fallback,
|
|
- combined zone temperature using configurable GREE/external sensor weighting and discrepancy protection,
|
|
- REST API and WebSocket updates,
|
|
- responsive PWA optimized for phones,
|
|
- JSON-based UI localization loaded from embedded `lang/*.json` language packs,
|
|
- light, dark and system appearance modes stored in a browser cookie,
|
|
- optional Bearer-token authentication,
|
|
- simulator mode for development without physical hardware,
|
|
- Debian/Ubuntu LXC systemd installer,
|
|
- Home Assistant custom integration that proxies `climate` commands through this controller,
|
|
- migration mapping generator for retaining existing HA entity IDs such as `climate.klima_salon`.
|
|
|
|
See [`BUILD_REPORT.md`](BUILD_REPORT.md) for package validation details and [`docs/LXC.md`](docs/LXC.md) for the LXC deployment/update workflow.
|
|
|
|
## Quick start
|
|
|
|
On Debian, Ubuntu or an LXC container:
|
|
|
|
```bash
|
|
unzip gree-controller-v0.3.5.zip
|
|
cd gree-controller
|
|
chmod +x scripts/*.sh
|
|
./scripts/dev.sh
|
|
```
|
|
|
|
`scripts/dev.sh` installs missing build tools when possible, installs stable Rust with `rustup` when required, creates `.env`, builds the application and starts the web panel.
|
|
|
|
Default address:
|
|
|
|
```text
|
|
http://HOST_ADDRESS:8787
|
|
```
|
|
|
|
The first empty database can be seeded with **Living Room (simulator)** so the UI, history, zones and automations can be tested without an AC.
|
|
|
|
### Development commands
|
|
|
|
```bash
|
|
./scripts/dev.sh --check
|
|
./scripts/dev.sh --release
|
|
./scripts/dev.sh --reset
|
|
./scripts/dev.sh --host 0.0.0.0 --port 8787
|
|
./scripts/dev.sh --no-install
|
|
```
|
|
|
|
## Web interface
|
|
|
|
The UI is mobile-first and uses no external CDN.
|
|
|
|
Language selector:
|
|
|
|
- English is the required default/fallback language,
|
|
- Polish is included,
|
|
- additional languages are discovered automatically from `lang/*.json` at build time.
|
|
|
|
The selected language is stored in the `gree_controller_language` cookie. The UI contains no hard-coded list of supported languages. To add a language, copy `lang/en.json`, translate the `translations` values, set the `meta` fields, save it as `<code>.json`, and rebuild. For example, `lang/de.json` becomes an additional language after `cargo build` / `./scripts/dev.sh`. Missing translation keys fall back to English.
|
|
|
|
See [`docs/LOCALIZATION.md`](docs/LOCALIZATION.md) for the language-pack format and validation rules.
|
|
|
|
Appearance selector:
|
|
|
|
- System,
|
|
- Light,
|
|
- Dark.
|
|
|
|
The selected appearance is stored in the `gree_controller_theme` cookie. The interface uses flat surfaces and borders; decorative UI shadows were removed in v0.2.0.
|
|
|
|
## Connecting a physical GREE device
|
|
|
|
1. Put the controller and AC in a network where UDP/7000 traffic is allowed.
|
|
2. Open the web interface and select **Discover**.
|
|
3. Start with **Auto (V1 + V2)** and 3 scan passes. The controller recognizes ECB responses and GCM responses carrying a `tag` and automatically binds discovered units.
|
|
4. If required, repeat discovery using **V1 AES-ECB** or **V2 AES-GCM** to isolate a model family.
|
|
5. New devices immediately open a naming step so you can enter room-friendly names such as **Salon** or **Sypialnia**. They can also be renamed later from **Devices -> Rename**. Re-discovery preserves your custom name.
|
|
6. If LXC/VLAN broadcast does not pass, add the unit manually with IP and MAC/CID.
|
|
|
|
Version 0.3.5 uses the EWPE/GREE protocol constants for ECB and GCM, the fixed GCM nonce/AAD, standard `cid=app`, correct packet `i` values, and a fresh scan before bind. A single lost UDP response no longer marks a device offline; three consecutive communication failures are required.
|
|
|
|
## LXC/systemd installation and updates
|
|
|
|
All operator scripts are under `scripts/`. On a clean Debian/Ubuntu LXC container:
|
|
|
|
```bash
|
|
chmod +x scripts/*.sh
|
|
sudo ./scripts/install.sh
|
|
```
|
|
|
|
The installer installs build dependencies/Rust when required, runs the Rust tests, builds a release binary, creates the `gree-controller` service account, stores runtime data in `/var/lib/gree-controller`, installs the binary under `/opt/gree-controller`, creates `/etc/gree-controller.env`, generates an administrator token and enables the systemd service. Existing `/etc/gree-controller.env` is preserved.
|
|
|
|
For a later release, unpack the new source archive and run:
|
|
|
|
```bash
|
|
sudo ./scripts/update.sh
|
|
```
|
|
|
|
The update is designed for LXC testing and production-style upgrades: compilation/tests happen before the running service is stopped; then the script backs up the installed binary, service unit, environment file and stopped SQLite database under `/var/backups/gree-controller/<timestamp>/`. After replacement it checks `/api/health`. A failed startup triggers automatic rollback to the previous binary, unit and database backup.
|
|
|
|
Service helpers:
|
|
|
|
```bash
|
|
./scripts/service.sh status
|
|
sudo ./scripts/service.sh restart
|
|
./scripts/service.sh logs
|
|
./scripts/service.sh health
|
|
```
|
|
|
|
Use `--skip-tests` with `install.sh` or `update.sh` only when you explicitly want to skip `cargo test --all-targets`. `scripts/install-lxc.sh` remains as a compatibility alias to `scripts/install.sh`.
|
|
|
|
## Environment configuration
|
|
|
|
| Variable | Default | Purpose |
|
|
|---|---:|---|
|
|
| `GREE_CONTROLLER_BIND` | `0.0.0.0:8787` | HTTP/WebSocket bind address |
|
|
| `GREE_CONTROLLER_DATABASE` | `./data/gree-controller.db` | SQLite file |
|
|
| `GREE_CONTROLLER_APP_TOKEN` | empty | Bearer token; empty disables API authentication |
|
|
| `GREE_CONTROLLER_SIMULATE` | `false` | Enables simulator seeding for development/testing |
|
|
| `GREE_CONTROLLER_AUTO_SEED` | `false` | Seeds a simulator into an empty database when simulation is enabled |
|
|
| `GREE_CONTROLLER_POLL_INTERVAL_SECONDS` | `15` | Device polling interval |
|
|
| `GREE_CONTROLLER_ZONE_INTERVAL_SECONDS` | `5` | Zone-control interval |
|
|
| `GREE_CONTROLLER_DISCOVERY_TIMEOUT_MS` | `3000` | UDP discovery timeout |
|
|
| `GREE_CONTROLLER_DISCOVERY_BROADCAST` | `255.255.255.255:7000` | Discovery broadcast target |
|
|
| `GREE_CONTROLLER_GREE_INTERFACE` | empty | Optional Linux interface used for all GREE UDP traffic, e.g. `eth1` |
|
|
| `GREE_CONTROLLER_ID` | `gree-controller` | Controller instance identifier used for logs/metadata; the GREE wire protocol uses the standard `cid=app` |
|
|
| `HA_URL` | empty | Optional Home Assistant URL |
|
|
| `HA_TOKEN` | empty | Optional Home Assistant Long-Lived Access Token |
|
|
| `HA_ENTITY_ID` | empty | Optional default HA temperature sensor |
|
|
|
|
Settings changed from the web panel are stored in SQLite. `GREE_CONTROLLER_APP_TOKEN` is loaded at process startup. When `GREE_CONTROLLER_DISCOVERY_BROADCAST` is explicitly present in the service environment, it overrides the persisted discovery target. Use `auto` together with `GREE_CONTROLLER_GREE_INTERFACE` to derive the subnet broadcast automatically.
|
|
|
|
## Per-zone room temperature sensors
|
|
|
|
Each control zone can pair one GREE indoor unit with its own optional room sensor from Home Assistant. This is intentionally configured per zone, so rooms do not share a global temperature source.
|
|
|
|
Example:
|
|
|
|
```text
|
|
Living room -> GREE Living Room + sensor.living_room_temperature
|
|
Bedroom -> GREE Bedroom + sensor.bedroom_temperature
|
|
Office -> GREE Office + sensor.office_temperature
|
|
```
|
|
|
|
Zone temperature strategies:
|
|
|
|
- **GREE only** — use the AC internal sensor.
|
|
- **GREE + room sensor** — recommended; calculate a weighted control temperature from both sensors. The default room-sensor weight is 40%.
|
|
- **Room sensor only** — use the assigned HA room sensor, with automatic fallback to GREE if HA or the entity becomes unavailable.
|
|
|
|
For combined control, `max_sensor_difference` protects against an obviously incorrect external measurement. If the two sensors differ by more than the configured threshold (default `3.0°C`), the zone uses the GREE temperature and logs a sensor-discrepancy event.
|
|
|
|
The zone API exposes `device_temperature`, `external_temperature`, `current_temperature` (the actual control temperature) and `control_temperature_source` for diagnostics. Existing SQLite zone records remain compatible because the new fields have defaults and are stored in the existing JSON payload.
|
|
|
|
Zone cards also expose quick `- / +` target controls and Heat/Cool buttons. These update the zone without opening the edit dialog and immediately push the changed mode/setpoint to the paired GREE unit without forcing power ON/OFF. Active schedules may still replace the zone setpoint while their time window is active.
|
|
|
|
## Home Assistant
|
|
|
|
There are two independent HA directions:
|
|
|
|
1. **HA as an optional sensor source**: the Rust controller can read a selected HA sensor and use it for a zone.
|
|
2. **HA as a client of GREE Controller**: the included custom integration creates `climate` entities whose commands are sent to this Rust application.
|
|
|
|
For the HA client connection, open **Settings -> Home Assistant integration access** in GREE Controller and press **Create new token**. The secret is shown once. Paste that token into the Home Assistant `GREE Controller` integration together with the controller URL. Managed HA tokens are stored as SHA-256 hashes and are scoped to the dedicated HA device/command API; they cannot change controller settings or manage other tokens.
|
|
|
|
The second option is designed to replace the built-in/default GREE integration without changing automation/dashboard references.
|
|
|
|
Example migration target:
|
|
|
|
```text
|
|
old: climate.klima_salon -> built-in GREE integration
|
|
new: climate.klima_salon -> GREE Controller custom integration -> Rust API -> AC
|
|
```
|
|
|
|
Generate an entity mapping:
|
|
|
|
```bash
|
|
./scripts/generate_ha_migration.py \
|
|
--entity climate.klima_salon \
|
|
--device gree-aabbccddeeff
|
|
```
|
|
|
|
Or validate against the controller and automatically use its only device:
|
|
|
|
```bash
|
|
./scripts/generate_ha_migration.py \
|
|
--entity climate.klima_salon \
|
|
--controller-url http://192.168.1.20:8787 \
|
|
--controller-token YOUR_CONTROLLER_TOKEN
|
|
```
|
|
|
|
The generated file must be copied to:
|
|
|
|
```text
|
|
/config/gree_controller_entities.json
|
|
```
|
|
|
|
Full installation and safe takeover procedure: [`home-assistant/README.md`](home-assistant/README.md) and [`docs/HOME_ASSISTANT_MIGRATION.md`](docs/HOME_ASSISTANT_MIGRATION.md).
|
|
|
|
## API
|
|
|
|
Important endpoints:
|
|
|
|
```text
|
|
GET /api/health
|
|
GET /api/bootstrap
|
|
POST /api/discovery
|
|
GET /api/devices
|
|
POST /api/devices
|
|
GET /api/devices/:id
|
|
PATCH /api/devices/:id
|
|
POST /api/devices/:id/bind
|
|
POST /api/devices/:id/poll
|
|
POST /api/devices/:id/command
|
|
GET /api/zones
|
|
POST /api/zones
|
|
GET /api/schedules
|
|
POST /api/schedules
|
|
GET /api/automations
|
|
POST /api/automations
|
|
GET /api/readings
|
|
GET /api/events
|
|
GET /api/settings
|
|
PUT /api/settings
|
|
GET /api/access-tokens
|
|
POST /api/access-tokens
|
|
DELETE /api/access-tokens/{id}
|
|
POST /api/integrations/home-assistant/test
|
|
GET /api/integrations/home-assistant/devices
|
|
POST /api/integrations/home-assistant/devices/{id}/command
|
|
WS /ws
|
|
```
|
|
|
|
Example command:
|
|
|
|
```bash
|
|
curl -X POST http://127.0.0.1:8787/api/devices/sim-salon/command \
|
|
-H 'Content-Type: application/json' \
|
|
-H 'Authorization: Bearer YOUR_TOKEN' \
|
|
-d '{"power":true,"mode":"cool","target_temperature":22,"fan_speed":3}'
|
|
```
|
|
|
|
`/api/health` is public. The normal controller API and Web UI require `GREE_CONTROLLER_APP_TOKEN` only when that administrator token is configured. The dedicated `/api/integrations/home-assistant/*` client endpoints always require either a generated HA access token or the administrator token. WebSocket accepts only the administrator token using `?token=...`.
|
|
|
|
More examples: [`docs/API.md`](docs/API.md).
|
|
|
|
## Security
|
|
|
|
- The application does not terminate TLS. Use HTTPS reverse proxy or VPN on untrusted networks.
|
|
- Do not expose port 8787 directly to the public Internet.
|
|
- Use a long random `GREE_CONTROLLER_APP_TOKEN`.
|
|
- Protect `.env`, `/etc/gree-controller.env`, SQLite data and HA tokens.
|
|
- The controller sends device commands only to configured local-network devices.
|
|
|
|
## Backup
|
|
|
|
For a systemd installation:
|
|
|
|
```bash
|
|
systemctl stop gree-controller
|
|
cp /var/lib/gree-controller/gree-controller.db /safe/backup/location/
|
|
systemctl start gree-controller
|
|
```
|
|
|
|
Development data is stored under `data/` by default.
|
|
|
|
## Project layout
|
|
|
|
```text
|
|
src/api.rs HTTP API, WebSocket, embedded web assets
|
|
src/db.rs SQLite persistence and row/domain mapping
|
|
src/queries.rs all SQLite schema and SQL statements
|
|
src/engine.rs polling, zones, schedules and automations
|
|
src/protocol/ GREE UDP/AES discovery, bind, status, command
|
|
src/home_assistant.rs optional HA sensor client
|
|
web/ mobile-first bilingual PWA
|
|
home-assistant/custom_components/ HA custom integration
|
|
scripts/install.sh first LXC/systemd installation
|
|
scripts/update.sh safe LXC/systemd update with backup/rollback
|
|
scripts/service.sh service status/start/stop/restart/logs/health
|
|
scripts/dev.sh development build/run/check workflow
|
|
scripts/generate_ha_migration.py legacy HA entity-ID mapping generator
|
|
scripts/smoke.sh API smoke test
|
|
systemd/ systemd service unit
|
|
```
|
|
|
|
## License
|
|
|
|
MIT. This project uses a community-reconstructed local device protocol and is not an official product of GREE Electric Appliances Inc.
|
|
|
|
|
|
## Multi-NIC / dedicated GREE interface
|
|
|
|
For LXC hosts with a management NIC and a separate GREE/VLAN NIC, bind GREE UDP traffic explicitly to the GREE interface:
|
|
|
|
```env
|
|
GREE_CONTROLLER_GREE_INTERFACE=eth1
|
|
GREE_CONTROLLER_DISCOVERY_BROADCAST=auto
|
|
GREE_CONTROLLER_SIMULATE=false
|
|
GREE_CONTROLLER_AUTO_SEED=false
|
|
```
|
|
|
|
`GREE_CONTROLLER_GREE_INTERFACE` is resolved to the interface current IPv4 address whenever a UDP socket is created, so DHCP address changes do not require hard-coding the source IP. `GREE_CONTROLLER_DISCOVERY_BROADCAST=auto` derives the subnet broadcast from the selected interface and overrides an older runtime value stored in SQLite.
|
|
|
|
Run `sudo ./scripts/network-debug.sh eth1 10.87.65.127` to verify addressing and routing.
|