${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=>`${esc(preset==='sleep'?tr('zones.sleepNow'):zonePresetLabel(preset))} `).join('')}
-
${esc(tr('zones.followHouseShort'))} ${esc(modeLabel('heat'))} ${esc(modeLabel('cool'))}
+
${esc(tr('zones.followHouseShort'))} ${esc(modeLabel('heat'))} ${esc(modeLabel('cool'))}
${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 @@
- 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 @@
Name
Device
Copy thermostat settings from… Copy settings
-
Mode policy Follow global mode Cooling only Heating only Manual target °C
+
Mode policy Follow global mode Cooling only Heating only
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 Comfort Sleep Away
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')];