Files
gree-controller/docs/API.md
T
2026-08-24 16:12:31 +02:00

11 KiB

API examples

TOKEN is optional when GREE_CONTROLLER_APP_TOKEN is empty.

AUTH='Authorization: Bearer TOKEN'
BASE='http://127.0.0.1:8787'

Home Assistant access tokens

Create and revoke integration tokens from the controller Web UI under Settings -> Home Assistant integration access. The clear-text secret is returned only once and the SQLite database stores only its SHA-256 hash.

Administrator endpoints:

GET    /api/access-tokens
POST   /api/access-tokens
DELETE /api/access-tokens/{id}

The Home Assistant custom integration uses a restricted API surface:

GET    /api/integrations/home-assistant/devices
POST   /api/integrations/home-assistant/devices/{id}/command
GET    /api/integrations/home-assistant/control-plan
POST   /api/integrations/home-assistant/zones/{id}/control

These endpoints always require Authorization: Bearer <generated-token> (or the administrator GREE_CONTROLLER_APP_TOKEN). A generated HA token cannot update settings, run discovery, delete devices, manage tokens, or use the controller WebSocket.

Discovery

curl -X POST "$BASE/api/discovery" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"timeout_ms":6000,"broadcast":"255.255.255.255:7000","protocol_version":0,"passes":3}'

protocol_version is 0 for auto/both, 1 for AES-ECB only, and 2 for AES-GCM only. passes is 1..10. Auto is recommended when different GREE Wi-Fi module generations share the network.

Device command

{
  "power": true,
  "mode": "cool",
  "target_temperature": 22.0,
  "fan_speed": 3,
  "swing_vertical": true,
  "quiet": false,
  "turbo": false,
  "light": true,
  "xfan": false,
  "air": false,
  "health": false,
  "sleep": false
}

Supported modes: auto, cool, dry, fan, heat. Fan speed: 0..5. Optional properties map to GREE Blo (X-FAN), Air, Health and SwhSlp (Sleep) and should only be used when the device reports the matching supports_* capability. Physical GREE Celsius setpoints are normalized to whole degrees in the 8..30°C range.

Smart thermostat / house control

The controller separates the seasonal house mode from per-zone profiles. Normal operation uses setpoint modulation: units remain powered while the controller moves the device target between an active and satisfied setpoint. off is the explicit hard-off house mode.

curl -X POST "$BASE/api/house/control" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"mode":"cool"}'

curl -X POST "$BASE/api/house/preset" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"preset":"sleep"}'

House modes: cool, heat, off. House presets: auto, comfort, sleep, away. A non-auto house preset creates temporary per-zone overrides that expire at each zone's next schedule boundary.

A zone stores separate profile temperatures for both seasons:

{
  "name": "Paweł",
  "device_id": "gree-aabbccddeeff",
  "enabled": true,
  "mode": "cool",
  "inherit_house_mode": true,
  "setpoint": 23.0,
  "cool_comfort_setpoint": 23.0,
  "cool_sleep_setpoint": 24.5,
  "cool_away_setpoint": 27.0,
  "heat_comfort_setpoint": 21.0,
  "heat_sleep_setpoint": 19.0,
  "heat_away_setpoint": 17.0,
  "hysteresis": 0.6,
  "min_adjust_seconds": 120,
  "standby_offset_c": 2.0,
  "smart_fan": true,
  "sensor_source": "combined",
  "ha_entity_id": "sensor.pawel_temperature",
  "external_sensor_weight": 0.4,
  "max_sensor_difference": 3.0
}

Quick control does not directly fight the schedule engine:

# Sleep now until the next schedule boundary
curl -X POST "$BASE/api/zones/ZONE_ID/control" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"preset":"sleep"}'

# Return to automatic schedule
curl -X POST "$BASE/api/zones/ZONE_ID/control" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"preset":"auto"}'

# Temporary custom room target
curl -X POST "$BASE/api/zones/ZONE_ID/control" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"setpoint":22.5}'

mode on the quick zone endpoint accepts house, cool, or heat. Global off always wins over local zone mode overrides.

Ready-made schedule templates

Templates generate normal schedule records and replace the current schedules for the selected zone. Every generated entry remains editable.

curl -X POST "$BASE/api/zones/ZONE_ID/schedule-template" -H "$AUTH" -H 'Content-Type: application/json' \
  -d '{"template":"child"}'

Templates: family, child, bedroom, workday, always.

Schedules use a profile (comfort, sleep, away) or a custom temperature:

{
  "zone_id": "UUID",
  "name": "Sleep",
  "enabled": true,
  "weekdays": [1,2,3,4,5,6,7],
  "start_time": "20:30",
  "end_time": "06:30",
  "preset": "sleep",
  "setpoint": 24.0
}

For non-custom schedules, setpoint is only a compatibility value; the actual target comes from the zone's seasonal profile.

Home Assistant outdoor assist and local HTTPS

Runtime settings can specify home_assistant.outdoor_entity_id and outdoor_assist_enabled. Outdoor temperature never replaces the zone room temperature. It is only used to make active setpoints/fan speed slightly more assertive in extreme weather.

For trusted local Home Assistant servers with self-signed/invalid HTTPS certificates, set:

{
  "home_assistant": {
    "url": "https://10.87.65.2",
    "allow_invalid_tls": true
  }
}

This setting is opt-in and applies only to the controller's outbound Home Assistant sensor client.

Current control plan

GET /api/control-plan returns a machine-readable view of what the controller is doing now and what is expected next. It includes the house mode/strategy, each zone's effective mode, configured_mode, inherit_house_mode, preset/current and target temperatures, current schedule, manual override expiry, upcoming schedule transitions, enabled automation rules and predictable time-triggered automation events.

curl "$BASE/api/control-plan" -H "$AUTH"

The restricted Home Assistant equivalent is GET /api/integrations/home-assistant/control-plan. HA may change a zone target/preset/mode/enabled state through:

curl -X POST "$BASE/api/integrations/home-assistant/zones/ZONE_ID/control" \
  -H 'Authorization: Bearer HA_TOKEN' -H 'Content-Type: application/json' \
  -d '{"setpoint":22.5}'

Settings backup and debug

GET /api/settings/export downloads configuration JSON (settings, devices, zones, schedules and automations). It intentionally excludes metric history, event logs and generated API-token records. The export includes GREE device binding keys plus configured Home Assistant and InfluxDB credentials, so treat it as a secret.

POST /api/settings/import accepts that JSON format and replaces application configuration while preserving metric history, events and generated API tokens.

Debug overlay state can be read or changed independently of the full settings document:

GET /api/debug
PUT /api/debug

Example body: {"overlay_enabled":true,"gree_frames":true}. With the overlay enabled, live api.request WebSocket events contain only HTTP method, path, status and duration. When GREE frame debug is enabled, sanitized gree.frame events are also emitted; the bound encryption key is not exposed.

WebSocket

Connect to ws://HOST:8787/ws?token=TOKEN. The first message uses event type bootstrap; later events include device.updated, zone.updated, settings.updated, debug.settings, api.request, gree.frame and log.created.

Localization assets

Localization endpoints are public because the login dialog also needs translations. Language packs are embedded in the Rust binary at build time.

curl "$BASE/lang/index.json"
curl "$BASE/lang/en.json"

GET /lang/index.json returns the automatically generated language catalog. GET /lang/<code>.json returns the corresponding language pack. Add a valid lang/<code>.json file and rebuild to expose a new language.

GET /api/history

The history API exposes three independent data families and a combined overview. hours may cover up to 10 years and limit is capped at 20,000 rows. Query buckets become progressively wider: 30 s (<=6 h), 2 min (<=24 h), 10 min (<=7 d), 30 min (<=30 d), 2 h (<=90 d), 6 h (<=1 y), then 24 h.

GET /api/history?scope=overview&hours=24
GET /api/history?scope=devices&device_id=DEVICE_ID&hours=2160
GET /api/history?scope=zones&zone_id=ZONE_ID&hours=8760
GET /api/history?scope=sensors&entity_id=sensor.room_temperature&hours=24

Local SQLite is the hot store. When compaction is enabled, full-resolution samples are kept for 24 h, then one sample per 10-minute bucket through day 7 and one per 30-minute bucket afterwards. history_retention_days controls final local pruning.

When InfluxDB is enabled, samples are written in parallel. Maintenance also backfills compacted legacy SQLite samples older than influxdb.history_threshold_days; those local rows are deleted only after the archive batch is accepted. Requests that cross the threshold read the older portion from InfluxDB 1.x or 2.x and merge it with recent SQLite data. If the Influx query fails, the endpoint falls back to the available SQLite history and returns storage_warning.

scope=overview returns zones, devices, sensors, row counts, bucket_seconds, plus storage/optional storage_warning. Device rows contain indoor/outdoor/target/power; zone rows contain GREE/external/control/target/device-setpoint/outdoor/power/mode/fan/demand/source/preset; HA rows contain entity, optional zone, kind and temperature.

InfluxDB long-term storage

Runtime settings support either InfluxDB 1.x (version=1, URL, database and optional username/password) or InfluxDB 2.x (version=2, URL, org, bucket and token). The same values can be supplied with GREE_CONTROLLER_INFLUX_* environment variables; see .env.example. An explicitly configured environment value overrides the persisted setting at startup.

Event log retention

GET /api/events/retention returns the current event/debug log retention window. PUT /api/events/retention accepts {"days":30} (1-3650), persists it and immediately prunes older event rows. The same value is part of runtime settings as event_log_retention_days and can be set at startup with GREE_CONTROLLER_EVENT_LOG_RETENTION_DAYS.

Night mode and sensor aliases

Runtime settings include night_mode with enabled, start_time, end_time, max_fan_speed (1-5), force_quiet and use_native_sleep. Overnight windows crossing midnight are supported. During the window thermostat-generated fan commands are limited to the configured maximum; Auto is converted to a bounded low speed, while Quiet and native Sleep are requested only when the unit supports them.

home_assistant.sensor_aliases is a map of Home Assistant entity_id to a friendly UI name, for example {"sensor.gabinet_temperature":"Gabinet"}. Aliases affect labels in the controller UI/history only. The backend canonicalizes aliases back to their original keys before Home Assistant requests and metric storage; an alias accidentally saved in a sensor field is also resolved defensively.