From dcdb821c601e1aa74b11f820ef92e391bf9ce9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Tue, 25 Aug 2026 19:21:27 +0200 Subject: [PATCH] v0.7.3 --- BUILD_REPORT.md | 10 ++++- Cargo.lock | 4 +- Cargo.toml | 2 +- FILE_MANIFEST.sha256 | 22 ++++----- README.md | 12 ++++- .../gree_controller/manifest.json | 2 +- lang/en.json | 11 ++++- lang/pl.json | 11 ++++- web/app.js | 45 ++++++++++++++----- web/index.html | 4 +- web/styles.css | 42 ++++++++++++----- web/sw.js | 2 +- 12 files changed, 120 insertions(+), 47 deletions(-) diff --git a/BUILD_REPORT.md b/BUILD_REPORT.md index 7b9f1db..a0d3fc6 100644 --- a/BUILD_REPORT.md +++ b/BUILD_REPORT.md @@ -1,4 +1,4 @@ -# GREE Controller v0.7.1 - build and validation report +# GREE Controller v0.7.2 - build and validation report ## Scope @@ -245,3 +245,11 @@ Validation for the 0.7.1 package: - Quick controls use explicit 3/4/5-column grids with no horizontal overflow behavior; all compact option buttons share the same 28 px minimum height, 9 px typography and 4 px gap. - User-facing inherited-mode labels are `Globalny/Global`; the internal/API value remains `house`, preserving backward compatibility. - No thermostat, schedule, automation, group, database or device-control logic was changed in 0.7.1. + +## 0.7.2 zone configuration separation + +- Reworked the Zones list into read-only status + configuration cards. +- Removed live temperature, preset, mode and enable/disable controls from the Zones list. +- Added navigation from a zone configuration card to its matching Dashboard quick thermostat. +- Kept backend zone-control endpoints and thermostat logic unchanged. +- Kept the legacy `setpoint` field in form submission as a hidden compatibility value while profile temperatures remain the explicit configuration surface. diff --git a/Cargo.lock b/Cargo.lock index c7415b6..8c5b84d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,7 +633,7 @@ dependencies = [ [[package]] name = "gree-controller" -version = "0.7.1" +version = "0.7.2" dependencies = [ "aes", "aes-gcm", @@ -1807,7 +1807,7 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.19" +version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ diff --git a/Cargo.toml b/Cargo.toml index 0cf63c8..c5d72db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gree-controller" -version = "0.7.1" +version = "0.7.2" 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 9ede619..106705d 100644 --- a/FILE_MANIFEST.sha256 +++ b/FILE_MANIFEST.sha256 @@ -1,10 +1,10 @@ d67af429e4da9ce08e9d2f2a8472849ffbd70d135b1c5da535a076026794d04c ./.env.example a4ec3874a2e3ab1bad28fb40bb620f7b01f64d01ad9b699306bf70ada31227db ./.gitignore -6145cb2513f1813a252721b295d7b98b48c916c4e6f29e1ea83b1401ba24a817 ./BUILD_REPORT.md -9206ce86a9de52a5bfae90c1b9c0d820d4b60c3f871596f6850d8ccc8ffa0beb ./Cargo.lock -0dffb9821d84c54f9f6115cdfe38c1549f70bf5f470c660fef1f819eacb9247f ./Cargo.toml +184ddfffab6226d0966c4b4a33715adb9df8a7b251810c65ba3297b02e008b9d ./BUILD_REPORT.md +c533ad8558fe581fbc357f7098605c42ff508e6b1253a5d651e5205dd5c347c7 ./Cargo.lock +fac8e8ca6bad6375be66e3da712deb943fa3bb44c78e84cfc99aef2ee3ea21c9 ./Cargo.toml 19b2943504acb8f8de280f873a8dbec4bb6ebbe3870b158f5655d4fb8c298f5f ./LICENSE -34fb4263e9248c6887020502c61c43825a0d396e5fe4c071de8875ad42af7d5e ./README.md +40257d2ace2752e53c6119ef5d09e4b98433cb1b86b5a3d0a69e62cc888a8a48 ./README.md f2c8eb28fedb26bb8f5636e1805e7a782cc472b87d882f6752d520f16216a248 ./build.rs cd45c288c3e002925656f7e06656034fd07013774f4ba0a00ba2bfb1fe60195a ./docs/API.md 234dd200e380a13ecd3e61b4ea455f6f08d64ce89382077dee80684acadb9703 ./docs/HOME_ASSISTANT_MIGRATION.md @@ -23,7 +23,7 @@ b7f0873109c52be9d7f09bea3dffc416103c50085e1f0680d11661a969479898 ./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 -fc73252d4c9d6b747fb6290adcd6fd1063d87e2a75e12437c7359dc80245958d ./home-assistant/custom_components/gree_controller/manifest.json +a8cd36e29f59a4df7bc9c37098539a143db18f383c1ae5e657129e14452cd6b0 ./home-assistant/custom_components/gree_controller/manifest.json d0206766d76f70ea1c75a618e41bf1e8e8c0429f42ee97f40daac1ea12f16658 ./home-assistant/custom_components/gree_controller/number.py 459233ccefa7827fc1c6887146d8a69e01149a497635fd5e1802c220f418eb28 ./home-assistant/custom_components/gree_controller/select.py ec35191e27a018a72cb341914e2abd7fecd25f6b641ea25bebb9e809dd8ecaff ./home-assistant/custom_components/gree_controller/sensor.py @@ -32,8 +32,8 @@ ec35191e27a018a72cb341914e2abd7fecd25f6b641ea25bebb9e809dd8ecaff ./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 -00a4d668e9665c7e98211e19974ded8aaf7bc5e89f2d29688d9080e4cd34ef3b ./lang/en.json -44923d70326c62f1e766928360ea4b4909f82b240e5f3773ef44a37f02b75ac4 ./lang/pl.json +cad2eb081cf7fbed275507763aafe288f25ae527c6775c84b89c1ac095d6843b ./lang/en.json +694c88c40860f7b14bcc6bd0575b6a53062302a4f1c29b7cb5829c00028dfa03 ./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 f74ac398c6f2e64ebb965900330324f3ec7403e70339638a408229f29fbd07ae ./src/state.rs b92a6cb158b494fe145b43c7641e65f6fafff47201d7d76edbec2cfd8b94835c ./systemd/gree-controller.service -e8776757097634bf09d75705a93b4225f619345f3b9bf242e37f1948a7edf394 ./web/app.js +27c39b66faab98b71ea515715c3744105f26bc274587aa4e6d595e16f3674f22 ./web/app.js e98bdd7204349cce1ec6f57283509697af0bbc72280622a6c3efa6fed242db4f ./web/favicon.svg -ff8c37afb1091b46a5a37683d352226d8cb5bc7ca3e2c8a11ee22d3144bd0494 ./web/index.html +57949bed2229ceb180ea681b0126b56f6e75dc325d7a89d384f823282736bdc7 ./web/index.html fd26156e9f1d6713d3def564ad000553d9a16a24376059701db2ee762c99ee6c ./web/manifest.webmanifest -e624f7673e0f0fa44f49771e535db5f360ad766e411fb0a810b572459e4617d4 ./web/styles.css -a80e68a9afc2cec5d12249cdf4c5cdb83abd96690fb58b306ec7f36d49ca70fa ./web/sw.js +e4ed0381250352f752e3b36133a89947db1adb08dec9cae7d2467684dbd1a807 ./web/styles.css +0e7a083b418c651fb37d121b978fd974206dfc804aaab868b1f2e4294924be3f ./web/sw.js d505d793ce7cc9485b45b78bba1c0d51887adc7451ab59a42702946e5b991382 ./web/theme-init.js diff --git a/README.md b/README.md index ac05ff4..8e1ceb4 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.1**. +Current version: **0.7.2**. ## 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.1.zip +unzip gree-controller-v0.7.2.zip cd gree-controller chmod +x scripts/*.sh ./scripts/dev.sh @@ -425,3 +425,11 @@ Zones now use setpoint modulation, global seasonal house mode, cooling/heating C - Renamed the compact zone mode from House to Global to make its meaning explicit: the zone inherits the global Heating/Cooling mode selected in GREE Controller. - Renamed Sleep now to Sleep in quick thermostat presets. - Advanced the PWA cache key to `gree-controller-v071-quick-controls`. + +## 0.7.2 zone configuration separation + +- The **Zones** view is now configuration-only: no live +/- temperature, preset or mode buttons are shown there. +- Current zone status remains visible as read-only context, together with mode policy, sensor strategy, hysteresis and Smart Fan configuration. +- The **Control** button opens the Dashboard, expands **Quick thermostats** and scrolls to the matching zone. +- The zone form no longer exposes the legacy/base `setpoint` as a live-looking control; profile temperatures remain configurable. +- Live zone temperature, preset, mode and enable/disable controls remain available in **Dashboard -> Quick thermostats**. diff --git a/home-assistant/custom_components/gree_controller/manifest.json b/home-assistant/custom_components/gree_controller/manifest.json index 99b58eb..f3caf23 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.1", + "version": "0.7.2", "config_flow": true, "integration_type": "hub", "iot_class": "local_polling", diff --git a/lang/en.json b/lang/en.json index 3b72f02..a3d6419 100644 --- a/lang/en.json +++ b/lang/en.json @@ -75,7 +75,7 @@ "devices.added": "Device added", "zones.automation": "Automation", "zones.new": "New zone", - "zones.description": "Each zone is a fast thermostat. It normally follows the house heating/cooling mode and changes the AC setpoint instead of cycling power.", + "zones.description": "Zones define thermostat logic and sensor configuration. Use Quick thermostats on the Dashboard for current temperature, preset and mode changes.", "zones.hysteresis": "Hysteresis °C", "zones.source": "Temperature strategy", "zones.greeSensor": "GREE only", @@ -579,6 +579,13 @@ "simulation.show": "Show", "simulation.allUnits": "All units", "simulation.allGroups": "All groups", - "groups.chooseMember": "Select at least one thermostat zone." + "groups.chooseMember": "Select at least one thermostat zone.", + "zones.configuration": "Zone configuration", + "zones.currentStatus": "Current status", + "zones.controlOnDashboard": "Current control is available in Quick thermostats on the Dashboard.", + "zones.controlNow": "Control", + "zones.noGroup": "no group", + "zones.coolOnly": "Cooling only", + "zones.heatOnly": "Heating only" } } diff --git a/lang/pl.json b/lang/pl.json index 5ab543c..3b24329 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -75,7 +75,7 @@ "devices.added": "Urządzenie dodane", "zones.automation": "Automatyka", "zones.new": "Nowa strefa", - "zones.description": "Każda strefa jest szybkim termostatem. Zwykle dziedziczy tryb grzania/chłodzenia domu i zmienia setpoint klimatyzatora zamiast cyklicznie wyłączać zasilanie.", + "zones.description": "Strefy definiują logikę termostatu i konfigurację czujników. Bieżącą temperaturę, profil i tryb zmieniaj w „Szybkich termostatach” na Pulpicie.", "zones.hysteresis": "Histereza °C", "zones.source": "Strategia temperatury", "zones.greeSensor": "Tylko GREE", @@ -579,6 +579,13 @@ "simulation.show": "Pokaż", "simulation.allUnits": "Wszystkie jednostki", "simulation.allGroups": "Wszystkie grupy", - "groups.chooseMember": "Wybierz co najmniej jedną strefę termostatu." + "groups.chooseMember": "Wybierz co najmniej jedną strefę termostatu.", + "zones.configuration": "Konfiguracja strefy", + "zones.currentStatus": "Stan bieżący", + "zones.controlOnDashboard": "Bieżące sterowanie znajduje się w Szybkich termostatach na Pulpicie.", + "zones.controlNow": "Steruj", + "zones.noGroup": "bez grupy", + "zones.coolOnly": "Tylko chłodzenie", + "zones.heatOnly": "Tylko grzanie" } } diff --git a/web/app.js b/web/app.js index 5bbe8d7..0ae67cf 100644 --- a/web/app.js +++ b/web/app.js @@ -686,24 +686,41 @@ function zoneCard(zone, detailed = true) { const target = Number(zone.effective_setpoint ?? zone.setpoint); const manual = zone.manual_preset || 'auto'; const mode = zone.inherit_house_mode ? 'house' : zone.mode; + const effectiveMode = zone.effective_mode || (zone.inherit_house_mode ? (app.settings?.house_mode || 'off') : zone.mode) || 'off'; const hasManualOverride = zone.manual_preset != null || zone.manual_setpoint != null; const override = zone.manual_override_until ? `${tr('zones.overrideUntil')} ${new Date(zone.manual_override_until).toLocaleTimeString(locale(), {hour:'2-digit',minute:'2-digit'})}` : (hasManualOverride ? tr('zones.manualNoBoundary') : tr('zones.scheduleControl')); - const enabledControl = detailed - ? `${esc(state)}` - : ``; - const quickClass = detailed ? '' : ' quick-control-card quick-thermostat-control'; - const quickRow = detailed ? '' : ' quick-control-row'; - return `
-

${esc(zone.name)}

${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(zone.active_preset))}

${enabledControl}
+ + if (detailed) { + const groupNames = (app.groups || []).filter(group => (group.zone_ids || []).includes(zone.id)).map(group => group.name); + const groupText = groupNames.length ? groupNames.join(' · ') : tr('zones.noGroup'); + const policy = zone.inherit_house_mode ? tr('zones.followHouse') : tr(zone.mode === 'heat' ? 'zones.heatOnly' : 'zones.coolOnly'); + return `
+
${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(tr('zones.modePolicy'))}${esc(policy)}
+
${esc(tr('zones.source'))}${esc(zoneStrategyLabel(zone))}
+
${esc(tr('zones.hysteresis'))}${Number(zone.hysteresis ?? 0.6).toFixed(1)}°C
+
${esc(tr('zones.smartFan'))}${esc(tr(zone.smart_fan === false ? 'common.off' : 'common.on'))}
+
+
${esc(sensorDetails)}
+ +
`; + } + + return `
+

${esc(zone.name)}

${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(zone.active_preset))}

${esc(tr('zones.measurement'))}${fmtTemp(roomTemperature)}
${Number.isFinite(target)?target.toFixed(1):'--'}°C
${esc(tr('zones.deviceTarget'))}${fmtTemp(zone.device_setpoint)}
-
+
${['auto','comfort','sleep','away'].map(preset=>``).join('')}
-
+
${zone.demand ? esc(tr('zones.requesting')) : esc(tr('zones.satisfied'))}${esc(override)}
- ${detailed ? `
${esc(sensorDetails)}
` : ''}
`; } @@ -1685,6 +1702,14 @@ document.addEventListener('click', async event => { if (action === 'zone-mode') return sendZoneControl(button.dataset.id,{mode:button.dataset.value}); if (action === 'zone-preset') return sendZoneControl(button.dataset.id,{preset:button.dataset.value}); if (action === 'zone-enabled') return sendZoneControl(button.dataset.id,{enabled:button.dataset.value==='true'}); + if (action === 'zone-go-control') { + showView('dashboard', {scroll:false}); + const target = [...document.querySelectorAll('[data-zone-card]')].find(card => card.dataset.zoneCard === button.dataset.id); + const disclosure = target?.closest('details.dashboard-disclosure'); + if (disclosure) disclosure.open = true; + requestAnimationFrame(() => target?.scrollIntoView({behavior:'smooth', block:'center'})); + return; + } if (action === 'debug-clear') { app.debugLines = []; renderDebugOverlay(); return; } if (action === 'edit-group') return populateGroup(button.dataset.id); if (action === 'delete-group') return deleteEntity('groups', button.dataset.id, 'groups.group'); diff --git a/web/index.html b/web/index.html index 46ae346..6db1060 100644 --- a/web/index.html +++ b/web/index.html @@ -79,7 +79,7 @@
Automation

Zones

-

Each zone is a fast thermostat. It normally follows the house heating/cooling mode and changes the AC setpoint instead of cycling power.

+

Zones define thermostat logic and sensor configuration. Use Quick thermostats on the Dashboard for current temperature changes and presets.

@@ -375,7 +375,7 @@
-
+

If this zone should use the global Heating/Cooling mode selected in GREE Controller, leave “Follow global mode” unchanged. Cooling only / Heating only forces a fixed mode for this zone.

Comfort / sleep / away temperatures
Cooling
diff --git a/web/styles.css b/web/styles.css index 2dfe360..7618818 100644 --- a/web/styles.css +++ b/web/styles.css @@ -864,16 +864,34 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36 } [data-view="dashboard"] .dashboard-summary-card { margin-bottom:10px; } -/* 0.7.0: desktop fit and cleaner unit controls. */ -#zoneList .zone-thermostat .card-footer { align-items:flex-start; } -#zoneList .zone-thermostat .card-footer > small { - flex:1 1 auto; - min-width:0; - overflow:visible; - text-overflow:clip; - white-space:normal; - line-height:1.35; -} -#zoneList .zone-thermostat .card-menu { flex:0 0 auto; } +/* 0.7.2: Zones are configuration cards; live controls stay on the Dashboard. */ +.zone-config-card { display:grid; gap:12px; overflow:hidden; } +.zone-config-card.demanding { border-color:color-mix(in srgb,var(--accent) 38%,var(--line)); } +.zone-config-card.zone-disabled .zone-config-status { opacity:.68; } +.zone-config-status { display:grid; grid-template-columns:minmax(150px,.8fr) minmax(180px,1.2fr); align-items:center; gap:12px; padding:11px 12px; border:1px solid var(--line); border-radius:14px; background:var(--surface-muted); } +.zone-config-temperature > small, .zone-config-grid small { display:block; margin-bottom:4px; color:var(--muted); font-size:9px; text-transform:uppercase; letter-spacing:.06em; } +.zone-config-temperature > div { display:flex; align-items:baseline; gap:7px; } +.zone-config-temperature span { color:var(--muted); font-size:18px; } +.zone-config-temperature b { color:var(--muted); font-size:11px; } +.zone-config-temperature strong { font-size:23px; letter-spacing:-.03em; } +.zone-config-runtime { display:grid; justify-items:end; gap:4px; min-width:0; color:var(--text-soft); font-size:11px; text-align:right; } +.zone-config-runtime > span:not(.badge), .zone-config-runtime small { max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } +.zone-config-runtime small { color:var(--muted); } +.zone-config-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px; } +.zone-config-grid > div { min-width:0; padding:9px 10px; border:1px solid var(--line); border-radius:12px; background:var(--surface-muted); } +.zone-config-grid strong { display:block; overflow:hidden; font-size:11px; text-overflow:ellipsis; white-space:nowrap; } +#zoneList .zone-config-card .sensor-detail { margin-top:0; } +#zoneList .zone-config-card .card-footer { align-items:flex-start; margin-top:0; } +#zoneList .zone-config-card .card-footer > small { flex:1 1 auto; min-width:0; overflow:visible; text-overflow:clip; white-space:normal; line-height:1.35; } +#zoneList .zone-config-card .card-menu { flex:0 0 auto; flex-wrap:wrap; justify-content:flex-end; } +#zoneList .zone-config-card .card-menu .primary { color:var(--accent-text); background:var(--accent); } -/* 0.7.1: quick thermostats and quick manual control now use the shared quick-control classes above. */ +@media (max-width:620px) { + .zone-config-status { grid-template-columns:1fr; } + .zone-config-runtime { justify-items:start; text-align:left; } + .zone-config-grid { grid-template-columns:1fr; } + #zoneList .zone-config-card .card-footer { flex-direction:column; } + #zoneList .zone-config-card .card-menu { width:100%; justify-content:flex-start; } +} + +/* 0.7.1+: quick thermostats and quick manual control use the shared quick-control classes above. */ diff --git a/web/sw.js b/web/sw.js index 60eb77e..f1a7569 100644 --- a/web/sw.js +++ b/web/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'gree-controller-v071-quick-controls'; +const CACHE = 'gree-controller-v072-zone-config'; 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')];