This commit is contained in:
Mateusz Gruszczyński
2026-08-26 22:30:10 +02:00
parent 6a35096c5d
commit f8d6bc2304
21 changed files with 410 additions and 84 deletions
+16
View File
@@ -709,6 +709,8 @@ function zoneCard(zone, detailed = true) {
const manualTakeover = deviceManualOverride
? `<div class="manual-override-panel"><div><strong>${esc(tr('zones.manualDeviceControl'))}</strong><p>${esc(tr('zones.manualDeviceDescription'))}</p></div><button type="button" data-action="zone-resume-automation" data-id="${esc(zone.id)}">${esc(tr('zones.resumeAutomation'))}</button></div>`
: '';
const manualPowerDisabled = !device || device.enabled === false ? ' disabled' : '';
const manualUnitPower = `<div class="zone-manual-power"><small title="${esc(tr('zones.unitPowerManualHint'))}">${esc(tr('zones.unitPowerManual'))}</small><div class="quick-control-row quick-control-row-2"><button class="${device?.power ? 'active' : ''}" data-action="zone-device-power" data-id="${esc(zone.id)}" data-value="true"${manualPowerDisabled}>${esc(tr('common.on'))}</button><button class="${device && !device.power ? 'active' : ''}" data-action="zone-device-power" data-id="${esc(zone.id)}" data-value="false"${manualPowerDisabled}>${esc(tr('common.off'))}</button></div></div>`;
if (detailed) {
const groupNames = (app.groups || []).filter(group => (group.zone_ids || []).includes(zone.id)).map(group => group.name);
@@ -734,6 +736,7 @@ function zoneCard(zone, detailed = true) {
return `<article class="list-card zone-thermostat quick-control-card quick-thermostat-control ${zone.demand ? 'demanding' : ''} ${zone.enabled ? '' : 'zone-disabled'} ${deviceManualOverride ? 'manual-takeover' : ''}" data-zone-card="${esc(zone.id)}">
<div class="list-card-head"><div><h3>${esc(zone.name)}</h3><p>${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(zone.active_preset))}</p></div><button type="button" class="zone-enable-toggle ${zone.enabled ? 'active' : ''}" data-action="zone-enabled" data-id="${esc(zone.id)}" data-value="${zone.enabled ? 'false' : 'true'}" aria-label="${esc(tr(zone.enabled ? 'zones.disable' : 'zones.enable'))}"><span>${zone.enabled ? '✓' : '○'}</span>${esc(state)}</button></div>
${manualUnitPower}
<div class="thermostat-main"><div><small>${esc(tr('zones.measurement'))}</small><strong>${fmtTemp(roomTemperature)}</strong></div><div class="temperature-control compact"><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="-0.5"></button><div class="target-temp compact">${Number.isFinite(target)?target.toFixed(1):'--'}<small>°C</small></div><button data-action="zone-temperature" data-id="${esc(zone.id)}" data-delta="0.5">+</button></div><div><small>${esc(tr('zones.deviceTarget'))}</small><strong>${fmtTemp(zone.device_setpoint)}</strong></div></div>
<div class="preset-row quick-control-row quick-control-row-4">
${['auto','comfort','sleep','away'].map(preset=>`<button class="${manual===preset?'active':''}" data-action="zone-preset" data-id="${esc(zone.id)}" data-value="${preset}">${esc(preset==='sleep'?tr('zones.sleepNow'):zonePresetLabel(preset))}</button>`).join('')}
@@ -1111,6 +1114,18 @@ function updateDevice(device) {
if (index >= 0) app.devices[index] = device; else app.devices.push(device);
}
async function sendZoneManualPower(id, power) {
try {
const result = await api(`/api/zones/${encodeURIComponent(id)}/manual-power`, {method:'POST', body:{power}});
if (result.device) updateDevice(result.device);
if (result.zone) {
const index = app.zones.findIndex(item => item.id === result.zone.id);
if (index >= 0) app.zones[index] = result.zone; else app.zones.push(result.zone);
}
renderAll(); scheduleControlPlanLoad(); toast(tr('zones.unitPowerUpdated'));
} catch (error) { toast(error.message, true); }
}
async function sendZoneControl(id, patch) {
const sequence = (app.zoneControlSeq[id] || 0) + 1;
app.zoneControlSeq[id] = sequence;
@@ -1724,6 +1739,7 @@ document.addEventListener('click', async event => {
if (action === 'group-power') return sendGroupControl(button.dataset.id,{power:button.dataset.value==='true'});
if (action === 'group-mode') return sendGroupControl(button.dataset.id,{mode:button.dataset.value});
if (action === 'group-preset') return sendGroupControl(button.dataset.id,{preset:button.dataset.value});
if (action === 'zone-device-power') return sendZoneManualPower(button.dataset.id, button.dataset.value === 'true');
if (action === 'zone-temperature') { const zone=app.zones.find(v=>v.id===button.dataset.id); if(zone) { const base=Number(zone.manual_setpoint ?? zone.effective_setpoint ?? zone.setpoint); queueZoneTemperature(zone, base+Number(button.dataset.delta)); } return; }
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});
+10
View File
@@ -752,9 +752,16 @@ body.simulation-standalone [data-view="simulation"] { min-height:calc(100vh - 36
overflow:visible;
padding:2px 0 5px;
}
.quick-control-card .quick-control-row-2 { grid-template-columns:repeat(2,minmax(0,1fr)); }
.quick-control-card .quick-control-row-3 { grid-template-columns:repeat(3,minmax(0,1fr)); }
.quick-control-card .quick-control-row-4 { grid-template-columns:repeat(4,minmax(0,1fr)); }
.quick-control-card .quick-control-row-5 { grid-template-columns:repeat(5,minmax(0,1fr)); }
/* Polish mode labels are noticeably wider than Auto/Nawiew/Grzanie.
Give Cool/Dry more room instead of shrinking or clipping their text. */
.quick-device-control .mode-row.quick-control-row-5 {
grid-template-columns:minmax(0,.8fr) minmax(0,1.35fr) minmax(0,1.2fr) minmax(0,.85fr) minmax(0,.8fr);
}
.quick-device-control .mode-row.quick-control-row-5 button { padding-inline:3px; }
.quick-control-card .quick-control-row button {
min-width:0;
min-height:var(--quick-control-height);
@@ -773,6 +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 .thermostat-main { margin:8px 0 7px; }
.quick-thermostat-control .zone-enable-toggle { min-height:28px; padding:4px 8px; font-size:10px; }
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'gree-controller-v075-group-power-state';
const CACHE = 'gree-controller-v077-mode-label-fit';
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')];