From 25e1a854e379a81f75e56df2481a924d62a456d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 24 Aug 2026 23:58:44 +0200 Subject: [PATCH] v0.6.4 --- BUILD_REPORT.md | 12 ++++- Cargo.lock | 2 +- Cargo.toml | 2 +- FILE_MANIFEST.sha256 | 16 +++---- README.md | 4 +- home-assistant/README.md | 2 + .../gree_controller/climate.py | 46 ++++++++++++++++++- .../gree_controller/manifest.json | 2 +- web/sw.js | 2 +- 9 files changed, 71 insertions(+), 17 deletions(-) diff --git a/BUILD_REPORT.md b/BUILD_REPORT.md index dbed7b2..1d03f45 100644 --- a/BUILD_REPORT.md +++ b/BUILD_REPORT.md @@ -1,8 +1,8 @@ -# GREE Controller v0.6.3 - build and validation report +# GREE Controller v0.6.4 - build and validation report ## Scope -Version 0.6.3 includes the notification, reverse-proxy, mobile decimal-input, log filtering and thermostat-copy changes described below, the Rust type-safety hotfix for communication-failure notifications, and a clearer persistent visual state for the selected house climate mode. +Version 0.6.4 includes the notification, reverse-proxy, mobile decimal-input, log filtering and thermostat-copy changes described below, the Rust type-safety hotfix for communication-failure notifications, and a clearer persistent visual state for the selected house climate mode. ## Implemented changes @@ -114,3 +114,11 @@ Validation performed in the build environment: - 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._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._thermostat` ID is already occupied, the existing entity ID is retained and a warning is logged instead of overwriting another entity. + diff --git a/Cargo.lock b/Cargo.lock index 767c87a..90c82a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,7 +633,7 @@ dependencies = [ [[package]] name = "gree-controller" -version = "0.6.3" +version = "0.6.4" dependencies = [ "aes", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index aaf5a83..8aed271 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gree-controller" -version = "0.6.3" +version = "0.6.4" edition = "2021" authors = ["GREE Controller contributors"] description = "Standalone local GREE HVAC controller with Web UI, SQLite and Home Assistant sensor support" diff --git a/FILE_MANIFEST.sha256 b/FILE_MANIFEST.sha256 index c33b287..c81aa04 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -1,10 +1,10 @@ 623bd769512ef75f51e9bf7fb685d4d59ec84d7ccc75c826fe72c5538be737c5 ./.env.example a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore -afa44c1b588eddb83ad1300e62ef320bf0264ff05f19c53d42a761a8046c97b4 ./BUILD_REPORT.md -98bf4f4c86d8c6384ca55fac266f9ccae812680ebc6536063194e369669de627 ./Cargo.lock -0971917d970910ff09b0164a36e0c8f32b270ad3115a01a5bf4a53798c1e1854 ./Cargo.toml +58037959419a8de25bcf9095b020bc3ab4c1559b975764f8d24b6359ad063ad6 ./BUILD_REPORT.md +436fd46220ef759d8204f24bdd94157c965cc39976b0f9baa79cebb367ee5c85 ./Cargo.lock +53f3f794080df48e11f5726678f7817c225ab64001dd1081d48c5aa780b2a7d0 ./Cargo.toml 19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -58471cab1a26e1212864a497e9e7b2730d849b7dd175dec7458a5a69dec1eadb ./README.md +be12639245e0929b12d11946b58c1d74f261733724bc4096a9ac68b12df30f24 ./README.md f2c8eb28fedb26bb8f5636e1805e7a782cc472b87d882f6752d520f16216a248 ./build.rs 674f8cf1ab9f1a49188e20b8e3977f1e2b7e9b8ef975315266730c44a55bbb48 ./docs/API.md 234dd200e380a13ecd3e61b4ea455f6f08d64ce89382077dee80684acadb9703 ./docs/HOME_ASSISTANT_MIGRATION.md @@ -13,17 +13,17 @@ f2c8eb28fedb26bb8f5636e1805e7a782cc472b87d882f6752d520f16216a248 ./build.rs 47e18e6b1723f1f6eb2516be6d83bbebd31b8155d9d3cdfccc0973177f3d8441 ./docs/PROJECT_SPEC.md 7748b1230029439a405139750b2e0607b8e463bfd56ad86982394105069697c5 ./docs/REVERSE_PROXY.md 28f1403d84b01b37bebb72548aa39a75bb14bdbb611a43bfa761319957cd6f39 ./docs/SECURITY.md -925ffa324b1b2cfad7ca40354e8255548f70627f95c4bb345b957d3f7e53694c ./home-assistant/README.md +38ac4aec7b971ae7df8db77faf5f9cb9702710ddf9a97def448fbe9e9ff138b1 ./home-assistant/README.md f8e8559fe10fe523ac5bc9aac25c6e26e862f679d502e8f3c39f38a0a8e40911 ./home-assistant/custom_components/gree_controller/__init__.py b8e7c0dd722bfac1d1d8d5c2a4b5ff6b2d6fa89525b9ff5c121ee1d09623a7fd ./home-assistant/custom_components/gree_controller/api.py -b0314469ddb7c69ee4a96c677815af30cb97713e93911a05597f2fdc3d10f489 ./home-assistant/custom_components/gree_controller/climate.py +abcca32b090e5a5d02574a3a340e97321f9879ae6c94ef12eb1f42d967eae2d5 ./home-assistant/custom_components/gree_controller/climate.py 5e4aef2143e81bedb5a15dd4be5c71b64a3ab448ec6d33a20851edd098e5f529 ./home-assistant/custom_components/gree_controller/config_flow.py 071f721b75af2ed68812c9be3a62b7ae63771e56e567a569f59ca976f8ff42f1 ./home-assistant/custom_components/gree_controller/const.py 8dd51322798cad28d4d19938fe459aa2a4d3b64b075532149f3c8b62049a6c8b ./home-assistant/custom_components/gree_controller/coordinator.py 5a96fe8f5c035c34f1339370270cd078056202d09e236dec75735be11de92a7d ./home-assistant/custom_components/gree_controller/entity_map.py c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./home-assistant/custom_components/gree_controller/icon.png c4fb75c246db651087900ebfc2291ff41ac87652cd6194fc0b776b0005c1cbcf ./home-assistant/custom_components/gree_controller/logo.png -a789da0f9f7178916227aa1a52575687090c8e82b463a53f1e16edb13f44599c ./home-assistant/custom_components/gree_controller/manifest.json +0247514d881e563e45d272dbd10f2363ded1f3fb11b307a6ad9a0566dcff3648 ./home-assistant/custom_components/gree_controller/manifest.json d0206766d76f70ea1c75a618e41bf1e8e8c0429f42ee97f40daac1ea12f16658 ./home-assistant/custom_components/gree_controller/number.py 5c914c2faa18a9cb9eb442dc111c9416f89a630578f820beb021d5381aa1b2d0 ./home-assistant/custom_components/gree_controller/sensor.py 7c23374d1c82e4b361089386ff89265322b80def0f4db9d3fd2c039867033105 ./home-assistant/custom_components/gree_controller/switch.py @@ -66,5 +66,5 @@ e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon. 002c172392a00006a5128fc4cb881b0361a2d3e76298dbd80c0a013f4e1f9dde ./web/index.html fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest 80ea1c50c3729adb4dd392b12d73b4247659a8b6b948ee76bd2f5df213eae918 ./web/styles.css -b82ee4b915076be30211bc260a9bc33117e5b1890bde7f37ac45aa28d7a7cafe ./web/sw.js +03fdeecaa4c68308668c09077e5390286c27d2a8e296367bec6b002fb9b8b3e8 ./web/sw.js d505d793ce7cc9485b45b78bba1c0d51887adc7451ab59a42702946e5b991382 ./web/theme-init.js diff --git a/README.md b/README.md index 9bf6be4..dcbed02 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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.6.3**. +Current version: **0.6.4**. ## Highlights @@ -46,7 +46,7 @@ See [`BUILD_REPORT.md`](BUILD_REPORT.md) for package validation details and [`do On Debian, Ubuntu or an LXC container: ```bash -unzip gree-controller-v0.6.3.zip +unzip gree-controller-v0.6.4.zip cd gree-controller chmod +x scripts/*.sh ./scripts/dev.sh diff --git a/home-assistant/README.md b/home-assistant/README.md index 02c90a1..cd480e7 100644 --- a/home-assistant/README.md +++ b/home-assistant/README.md @@ -122,5 +122,7 @@ The recommended `combined` strategy keeps the GREE sensor as the primary input a Each controller zone is exposed as a full Home Assistant `climate` entity with current/target temperature and HVAC modes: Off, Auto (follow the controller house mode), Cool and Heat. The existing zone target `number` and enabled `switch` remain available for compatibility. +From version 0.6.4, zone climate entities use `climate._thermostat`, for example `climate.igor_thermostat`. On integration reload, existing zone climate registry entries are migrated to this scheme using the zone's current name. If the target entity ID is already occupied, the old ID is retained and Home Assistant logs a warning. + When the controller detects optional GREE properties, the integration also creates switches for supported features such as panel light, Quiet, Turbo, X-FAN, Air, Health and native Sleep. Reload the integration (or restart Home Assistant) after upgrading so newly added entity types are created. diff --git a/home-assistant/custom_components/gree_controller/climate.py b/home-assistant/custom_components/gree_controller/climate.py index 97c28e7..984d3f3 100644 --- a/home-assistant/custom_components/gree_controller/climate.py +++ b/home-assistant/custom_components/gree_controller/climate.py @@ -20,6 +20,7 @@ from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.util import slugify from . import GreeControllerRuntimeData from .const import DOMAIN @@ -73,11 +74,54 @@ async def async_setup_entry( for zone in runtime.coordinator.plan.get("zones", []): zone_id = str(zone.get("zone_id") or "").strip() if zone_id: + _ensure_zone_thermostat_entity_id(registry, zone) entities.append(GreeControllerZoneClimate(runtime.coordinator, zone_id)) async_add_entities(entities) +def _ensure_zone_thermostat_entity_id( + registry: er.EntityRegistry, zone: dict[str, Any] +) -> None: + """Keep zone climate entity IDs stable and descriptive. + + Zone climate entities use ``climate._thermostat``. Existing + registry entries are migrated to the same scheme, which also fixes stale + IDs left behind after a zone was renamed (for example ``climate.jan_2`` + for a zone currently named Igor). + """ + zone_id = str(zone.get("zone_id") or "").strip() + if not zone_id: + return + + unique_id = f"{zone_id}-zone-climate" + current_entity_id = registry.async_get_entity_id("climate", DOMAIN, unique_id) + if current_entity_id is None: + return + + zone_name = str(zone.get("zone_name") or zone_id).strip() or zone_id + object_id = slugify(zone_name) or slugify(zone_id) or "zone" + desired_entity_id = f"climate.{object_id}_thermostat" + if current_entity_id == desired_entity_id: + return + + occupied = registry.async_get(desired_entity_id) + if occupied is not None and occupied.entity_id != current_entity_id: + _LOGGER.warning( + "Cannot rename zone thermostat %s to %s because that entity ID is already in use", + current_entity_id, + desired_entity_id, + ) + return + + registry.async_update_entity(current_entity_id, new_entity_id=desired_entity_id) + _LOGGER.info( + "Renamed zone thermostat entity %s to %s", + current_entity_id, + desired_entity_id, + ) + + class GreeControllerClimate(CoordinatorEntity[GreeControllerCoordinator], ClimateEntity): """Home Assistant climate entity controlled through the Rust service.""" @@ -216,7 +260,7 @@ class GreeControllerZoneClimate(CoordinatorEntity[GreeControllerCoordinator], Cl """Full climate entity for a controller thermostat zone.""" _attr_has_entity_name = True - _attr_name = None + _attr_name = "Thermostat" _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_min_temp = 8.0 _attr_max_temp = 30.0 diff --git a/home-assistant/custom_components/gree_controller/manifest.json b/home-assistant/custom_components/gree_controller/manifest.json index bf91c30..d71fd76 100644 --- a/home-assistant/custom_components/gree_controller/manifest.json +++ b/home-assistant/custom_components/gree_controller/manifest.json @@ -1,7 +1,7 @@ { "domain": "gree_controller", "name": "GREE Controller", - "version": "0.6.3", + "version": "0.6.4", "config_flow": true, "integration_type": "hub", "iot_class": "local_polling", diff --git a/web/sw.js b/web/sw.js index a42351a..bfb1ff8 100644 --- a/web/sw.js +++ b/web/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'gree-controller-v063'; +const CACHE = 'gree-controller-v064'; const SCOPE = new URL(self.registration.scope).pathname.replace(/\/$/, ''); const path = value => `${SCOPE}${value.startsWith('/') ? value : `/${value}`}` || '/'; const ASSETS = [path('/'), path('/styles.css'), path('/app.js'), path('/theme-init.js'), path('/favicon.svg'), path('/manifest.webmanifest'), path('/lang/index.json'), path('/lang/en.json')];