diff --git a/BUILD_REPORT.md b/BUILD_REPORT.md index 07f728b..c4808ff 100644 --- a/BUILD_REPORT.md +++ b/BUILD_REPORT.md @@ -1,4 +1,4 @@ -# GREE Controller v0.7.7 - build and validation report +# GREE Controller v0.7.8 - build and validation report ## Scope @@ -374,3 +374,10 @@ Rust compiler note: this packaging environment still does not provide `cargo`, ` - 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. diff --git a/Cargo.lock b/Cargo.lock index 3eefd01..9d268ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,7 +633,7 @@ dependencies = [ [[package]] name = "gree-controller" -version = "0.7.7" +version = "0.7.8" dependencies = [ "aes", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index 17906e6..2dd364f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gree-controller" -version = "0.7.7" +version = "0.7.8" 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 16ee07e..d23219a 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -1,10 +1,10 @@ d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore -702331da239c3b8ef2166913dfa40340cb8c44a996b03983ccc7490a1089e898 ./BUILD_REPORT.md -401e399e01e9999501f14771d9b5a693b34bb1e51ba33a37b441248b35c60479 ./Cargo.lock -e9336e73b6279a5b58d6577c8784c23f19bc1ae0c0e2c7074e6781dfec5a61e3 ./Cargo.toml +ff0beb45aafde9e919deaa38a8900c8bc09ae5a853e1a17ef6a2b596eaa38a4e ./BUILD_REPORT.md +8d644882219821d356fdc1e65f2dfd6e15e0ac6ad4d83b6cc9e99b914d28afe1 ./Cargo.lock +5f372aa21533c57499cb6ce87db5708e5127bd8021b1113a0cc5393a8c74dd8e ./Cargo.toml 19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -9abaeb7f6c29ec8775ef6edeb683916323aa17ad83027bbe2b449456d682dd9c ./README.md +8d296b79780b105cce00d1ca447727316bb3363984129740ac1c6d3868b3b0b6 ./README.md f2c8eb28fedb26bb8f5636e1805e7a782cc472b87d882f6752d520f16216a248 ./build.rs 8e72ed628055338d8e9ecc3d9f6929f225fc1ce00fa2e0f19765594a1bcb824f ./docs/API.md 234dd200e380a13ecd3e61b4ea455f6f08d64ce89382077dee80684acadb9703 ./docs/HOME_ASSISTANT_MIGRATION.md @@ -23,7 +23,7 @@ f31f3909f03fe4be105f89bcb4fe86331b01757288ac342ed0d26b35422d6387 ./home-assista 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 -b89c0838188944f1054f3885a75cd5a35f4084b6a740f0fa3cb373967b5af61f ./home-assistant/custom_components/gree_controller/manifest.json +f37d31b0b823a6ee516ebc34599c52e19dafcf1e7074253d08913587f3c5294c ./home-assistant/custom_components/gree_controller/manifest.json d0206766d76f70ea1c75a618e41bf1e8e8c0429f42ee97f40daac1ea12f16658 ./home-assistant/custom_components/gree_controller/number.py a697d18173087621a666781137f07617d23c1fb2720d8b246637a804634d5299 ./home-assistant/custom_components/gree_controller/select.py 047f3db03f0db54a40d63092370e5861510d611edfb0a0659e4c25b949d47854 ./home-assistant/custom_components/gree_controller/sensor.py @@ -32,8 +32,8 @@ a697d18173087621a666781137f07617d23c1fb2720d8b246637a804634d5299 ./home-assista 13f30e2dcdcedbd1b6c3f99c2335e0487108fd72c8e86922368b84f2fa2038ae ./home-assistant/custom_components/gree_controller/translations/pl.json 4513070521d3dda0efb0d974a86ba674494cfb2b66fe9e5cac5b1b0430dede97 ./home-assistant/generated/gree_controller_entities.example.json 253a0bc912786e67ea7fc92a64e4a510ad973bec343a88ccfb1f28fca3e8cf01 ./lang/README.md -a30a0009e07f79531e1989a08c4585020368f558036f5b50630fa54ea5fe3e36 ./lang/en.json -439e58a61087c67baaae1f7d5d58620cab69fd630e7777c8ab827ad20f109522 ./lang/pl.json +62dcacc6f168edfaa861d8c5ff32883f35bcf0a045f6ee3899588a748427ce63 ./lang/en.json +79cda4f5b28ebf760e82b05aeef11a79c0129e895c4c1588344561d1a948bce3 ./lang/pl.json 028e1f16e9fbaed57cadb88eff04e65b4bd67722c50b4d6b1fb525f5a2f39abf ./make_zip.py bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./scripts/README.md 5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./scripts/common.sh @@ -62,10 +62,10 @@ a910bd9432a393740c0f6fab52bfcb551f0ea756718d66d290fd2610767cf07c ./src/protocol 6a1c0cab3eab80ecd254c5e486b1fba068523edb8d699c64054c17f67f4a31d0 ./src/queries.rs 48a001996a8e0f7ab847fad062f6973173fce2766bdfa50cbd49c07bcf628fa5 ./src/state.rs b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service -7bdca69f08f871b9fcf965751ccc6354704c1b1bf38fd94a04637ebc0bd2e881 ./web/app.js +b04c9a8d013a1d512cb5246b76ae84ca20770a4561cfa23f51109db01e6a468f ./web/app.js e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg 5e39be3a379e3036c03773d5f4cc22baed79e9688b574b438fe87b6f5828089a ./web/index.html fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest -ba3b35a38d76993140f37a3c1f05aa4271e83d57b365346db1e14d9ca4ee7d02 ./web/styles.css -68578c989fe37be717905eda435fd6d725345c28260aad06fe44bfb1116326ca ./web/sw.js +d4369e34b5392be142c038f5fc68343bd3514a8b34aa8503f1ed2ed700737299 ./web/styles.css +47189dc2da72f976e5e868fcc82284f7ee45b47747812ac08f54872e6d5b758d ./web/sw.js d505d793ce7cc9485b45b78bba1c0d51887adc7451ab59a42702946e5b991382 ./web/theme-init.js diff --git a/README.md b/README.md index 3c263a0..29e5f9a 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.7.7**. +Current version: **0.7.8**. ## Highlights @@ -47,7 +47,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.7.7.zip +unzip gree-controller-v0.7.8.zip cd gree-controller chmod +x scripts/*.sh ./scripts/dev.sh @@ -475,3 +475,10 @@ Zones now use setpoint modulation, global seasonal house mode, cooling/heating C - Adjusted the five-column quick-device mode row so longer Polish labels such as **Chłodzenie** and **Osuszanie** receive more width without reducing the whole row font size. - The change is limited to the dashboard quick-device mode row; thermostat/group layouts are unchanged. - Rotated the PWA cache key so browsers fetch the corrected stylesheet after upgrade. + + +## 0.7.8 Zone status wording and unit power + +- Zone runtime status now says **Zapotrzebowanie aktywne** when the thermostat is requesting work and **Cel osiągnięty** when the target/hysteresis condition is satisfied; **Komfort** remains only the preset name. Paused control is shown as **Oczekiwanie**, and missing control temperature as **Brak pomiaru**. +- Quick thermostats use one contextual **Włącz jednostkę / Wyłącz jednostkę** control instead of the redundant **Jednostka · pilot** caption and separate ON/OFF buttons. +- Direct unit power still behaves as manual/remote takeover and does not enable the whole group. diff --git a/home-assistant/custom_components/gree_controller/manifest.json b/home-assistant/custom_components/gree_controller/manifest.json index adcc0e2..0635f1f 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.7.7", + "version": "0.7.8", "config_flow": true, "integration_type": "hub", "iot_class": "local_polling", diff --git a/lang/en.json b/lang/en.json index eeb83df..a9c955d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -260,8 +260,8 @@ "zones.overrideUntil": "Manual until", "zones.scheduleControl": "Schedule control", "zones.manualNoBoundary": "Manual · no next transition", - "zones.requesting": "Comfort demand", - "zones.satisfied": "Satisfied / inverter standby", + "zones.requesting": "Demand active", + "zones.satisfied": "Target reached", "zones.quickHintSmart": "Quick changes last until the next schedule transition; without a schedule they stay active.", "preset.auto": "Auto", "preset.comfort": "Comfort", @@ -593,8 +593,11 @@ "zones.manualDeviceUntil": "Manual control / remote until {time}", "zones.manualDeviceNoBoundary": "Manual control / remote · until resumed", "zones.resumeAutomation": "Resume automation", - "zones.unitPowerManual": "Unit · remote", - "zones.unitPowerManualHint": "Turns only this unit on or off like a remote; it does not enable the whole group.", - "zones.unitPowerUpdated": "Single-unit power updated" + "zones.unitPowerManualHint": "Controls only this unit directly like a remote; it does not enable the whole group.", + "zones.unitPowerUpdated": "Single-unit power updated", + "zones.turnUnitOn": "Turn unit on", + "zones.turnUnitOff": "Turn unit off", + "zones.waiting": "Waiting", + "zones.noMeasurement": "No measurement" } } diff --git a/lang/pl.json b/lang/pl.json index 909070d..0180fc1 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -260,8 +260,8 @@ "zones.overrideUntil": "Ręcznie do", "zones.scheduleControl": "Sterowanie harmonogramem", "zones.manualNoBoundary": "Ręcznie · brak kolejnego przejścia", - "zones.requesting": "Potrzeba komfortu", - "zones.satisfied": "Komfort / standby inwertera", + "zones.requesting": "Zapotrzebowanie aktywne", + "zones.satisfied": "Cel osiągnięty", "zones.quickHintSmart": "Szybka zmiana działa do kolejnego przejścia harmonogramu; bez harmonogramu pozostaje aktywna.", "preset.auto": "Auto", "preset.comfort": "Komfort", @@ -593,8 +593,11 @@ "zones.manualDeviceUntil": "Sterowanie ręczne / pilot do {time}", "zones.manualDeviceNoBoundary": "Sterowanie ręczne / pilot · do wznowienia", "zones.resumeAutomation": "Wznów automatykę", - "zones.unitPowerManual": "Jednostka · pilot", - "zones.unitPowerManualHint": "Włącza lub wyłącza tylko tę jednostkę jak pilot; nie włącza całej grupy.", - "zones.unitPowerUpdated": "Zmieniono zasilanie pojedynczej jednostki" + "zones.unitPowerManualHint": "Steruje bezpośrednio tylko tą jednostką jak pilot; nie włącza całej grupy.", + "zones.unitPowerUpdated": "Zmieniono zasilanie pojedynczej jednostki", + "zones.turnUnitOn": "Włącz jednostkę", + "zones.turnUnitOff": "Wyłącz jednostkę", + "zones.waiting": "Oczekiwanie", + "zones.noMeasurement": "Brak pomiaru" } } diff --git a/web/app.js b/web/app.js index 6a880de..5ef7bf7 100644 --- a/web/app.js +++ b/web/app.js @@ -314,9 +314,7 @@ function renderControlPlan() { const target = zone.target_temperature == null ? '--' : Number(zone.target_temperature).toFixed(1); const groupNames = zoneGroups.get(zone.zone_id) || []; const scope = groupNames.length ? `${tr('groups.group')}: ${groupNames.join(' · ')}` : (zone.device_name || tr('common.noDevice')); - const planState = zone.device_manual_override - ? tr('zones.manualDeviceControl') - : (zone.enabled ? (zone.demand ? tr('zones.requesting') : tr('zones.satisfied')) : tr('common.disabled')); + const planState = zoneRuntimeStatusLabel(zone, zone.mode || 'off'); return `
${esc(scope)}

${esc(zone.zone_name)}

${esc(planState)}
${fmtTemp(zone.current_temperature)}${esc(target)}°C

${esc(houseModeLabel(zone.mode || 'off'))} · ${esc(zonePresetLabel(zone.preset))}${zone.current_schedule_name ? ` · ${esc(zone.current_schedule_name)}` : ''}

`; }).join(''); @@ -686,6 +684,14 @@ function zonePresetLabel(preset) { return tr(key) === key ? (preset || 'comfort') : tr(key); } +function zoneRuntimeStatusLabel(zone, effectiveMode) { + if (zone.device_manual_override === true) return tr('zones.manualDeviceControl'); + if (!zone.enabled) return tr('common.disabled'); + if ((effectiveMode || 'off') === 'off') return tr('zones.waiting'); + if (zone.current_temperature == null) return tr('zones.noMeasurement'); + return zone.demand ? tr('zones.requesting') : tr('zones.satisfied'); +} + function zoneCard(zone, detailed = true) { const device = app.devices.find(d => d.id === zone.device_id); const state = zone.enabled ? tr('common.active') : tr('common.disabled'); @@ -710,7 +716,7 @@ function zoneCard(zone, detailed = true) { ? `
${esc(tr('zones.manualDeviceControl'))}

${esc(tr('zones.manualDeviceDescription'))}

` : ''; const manualPowerDisabled = !device || device.enabled === false ? ' disabled' : ''; - const manualUnitPower = `
${esc(tr('zones.unitPowerManual'))}
`; + const manualUnitPower = `
`; if (detailed) { const groupNames = (app.groups || []).filter(group => (group.zone_ids || []).includes(zone.id)).map(group => group.name); @@ -720,7 +726,7 @@ function zoneCard(zone, detailed = true) {
${esc(tr('zones.configuration'))}

${esc(zone.name)}

${esc(device?.name || tr('common.noDevice'))} · ${esc(tr('groups.group'))}: ${esc(groupText)}

${esc(state)}
${esc(tr('zones.currentStatus'))}
${fmtTemp(roomTemperature)}${Number.isFinite(target) ? `${target.toFixed(1)}°C` : '—'}
-
${esc(zone.enabled ? (zone.demand ? tr('zones.requesting') : tr('zones.satisfied')) : tr('common.disabled'))}${esc(houseModeLabel(effectiveMode))} · ${esc(zonePresetLabel(zone.active_preset))}${esc(override)}
+
${esc(zoneRuntimeStatusLabel(zone, effectiveMode))}${esc(houseModeLabel(effectiveMode))} · ${esc(zonePresetLabel(zone.active_preset))}${esc(override)}
${esc(tr('zones.modePolicy'))}${esc(policy)}
@@ -742,7 +748,7 @@ function zoneCard(zone, detailed = true) { ${['auto','comfort','sleep','away'].map(preset=>``).join('')}
-
${deviceManualOverride ? esc(tr('zones.manualDeviceControl')) : (zone.demand ? esc(tr('zones.requesting')) : esc(tr('zones.satisfied')))}${esc(override)}
+
${esc(zoneRuntimeStatusLabel(zone, effectiveMode))}${esc(override)}
${manualTakeover} `; } diff --git a/web/styles.css b/web/styles.css index 2106fa1..5f7828e 100644 --- a/web/styles.css +++ b/web/styles.css @@ -780,9 +780,9 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36 .quick-device-control .temperature-control { gap:12px; padding:12px 0 10px; } .quick-device-control .current-line { margin:-3px 0 10px; font-size:11px; } .quick-device-control .device-toggles.quick-control-row { margin-top:1px; } -.quick-thermostat-control .zone-manual-power { margin-top:6px; } -.quick-thermostat-control .zone-manual-power > small { display:block; margin-bottom:2px; color:var(--muted); font-size:9px; } -.quick-thermostat-control .zone-manual-power button.active { color:var(--accent-text); background:var(--accent); font-weight:800; } +.quick-thermostat-control .zone-manual-power { display:flex; margin-top:6px; } +.quick-thermostat-control .zone-manual-power .zone-unit-power-toggle { width:100%; min-height:30px; font-size:10px; } +.quick-thermostat-control .zone-manual-power .zone-unit-power-toggle.active { color:var(--accent-text); background:var(--accent); font-weight:800; } .quick-thermostat-control .thermostat-main { margin:8px 0 7px; } .quick-thermostat-control .zone-enable-toggle { min-height:28px; padding:4px 8px; font-size:10px; } diff --git a/web/sw.js b/web/sw.js index df827a4..99fff4b 100644 --- a/web/sw.js +++ b/web/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'gree-controller-v077-mode-label-fit'; +const CACHE = 'gree-controller-v078-zone-status-unit-power'; 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')];