This commit is contained in:
Mateusz Gruszczyński
2026-08-27 17:17:05 +02:00
parent 65eaf50fb6
commit 62514258d3
16 changed files with 205 additions and 43 deletions
+24 -1
View File
@@ -47,6 +47,25 @@ const modeLabel = mode => tr(`mode.${mode}`) === `mode.${mode}` ? mode : tr(`mod
const houseModeLabel = mode => mode === 'off' ? tr('house.noControl') : modeLabel(mode);
const fanLabel = value => ({0:'fan.auto',1:'fan.low',2:'fan.mediumLow',3:'fan.medium',4:'fan.mediumHigh',5:'fan.high'}[value] ? tr({0:'fan.auto',1:'fan.low',2:'fan.mediumLow',3:'fan.medium',4:'fan.mediumHigh',5:'fan.high'}[value]) : value);
const dateTime = value => value ? new Intl.DateTimeFormat(locale(), {dateStyle:'short', timeStyle:'short'}).format(new Date(value)) : '—';
const localResumeSeconds = value => {
const timestamp = value ? new Date(value).getTime() : NaN;
return Number.isFinite(timestamp) ? Math.max(0, Math.ceil((timestamp - Date.now()) / 1000)) : null;
};
const formatCountdown = seconds => {
if (!Number.isFinite(seconds)) return '--:--';
const safe = Math.max(0, Math.floor(seconds));
const minutes = Math.floor(safe / 60);
const remainder = safe % 60;
return `${String(minutes).padStart(2,'0')}:${String(remainder).padStart(2,'0')}`;
};
function updateLocalThermostatCountdowns() {
$$('[data-local-resume-countdown]').forEach(node => {
const remaining = localResumeSeconds(node.dataset.localResumeCountdown);
node.textContent = remaining !== null && remaining > 0
? tr('zones.localThermostatOffDescriptionTimed', {time:formatCountdown(remaining)})
: tr('zones.localThermostatResuming');
});
}
const haSensorLabel = entity => app.sensorAliases?.[entity] || app.settings?.home_assistant?.sensor_aliases?.[entity] || entity;
function updateConnectionIndicator(status) {
@@ -713,6 +732,7 @@ function zoneCard(zone, detailed = true) {
const hasManualOverride = zone.manual_preset != null || zone.manual_setpoint != null;
const deviceManualOverride = zone.device_manual_override === true;
const localThermostatPower = zone.local_thermostat_power;
const localThermostatResumeAt = zone.local_thermostat_resume_at || null;
const localThermostatOverride = localThermostatPower === true || localThermostatPower === false;
const override = deviceManualOverride
? (zone.device_manual_override_until
@@ -726,7 +746,9 @@ 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-resume="device" data-id="${esc(zone.id)}">${esc(tr('zones.resumeAutomation'))}</button></div>`
: localThermostatOverride
? `<div class="manual-override-panel local-thermostat-panel"><div><strong>${esc(tr('zones.localThermostatControl'))}</strong><p>${esc(tr(localThermostatPower ? 'zones.localThermostatOnDescription' : 'zones.localThermostatOffDescription'))}</p></div><button type="button" data-action="zone-resume-automation" data-resume="local" data-id="${esc(zone.id)}">${esc(tr('zones.resumeAutomation'))}</button></div>`
? `<div class="manual-override-panel local-thermostat-panel"><div><strong>${esc(tr('zones.localThermostatControl'))}</strong>${localThermostatPower === false && localThermostatResumeAt
? `<p data-local-resume-countdown="${esc(localThermostatResumeAt)}">${esc(tr('zones.localThermostatOffDescriptionTimed', {time:formatCountdown(localResumeSeconds(localThermostatResumeAt))}))}</p>`
: `<p>${esc(tr(localThermostatPower ? 'zones.localThermostatOnDescription' : 'zones.localThermostatOffDescription'))}</p>`}</div><button type="button" data-action="zone-resume-automation" data-resume="local" data-id="${esc(zone.id)}">${esc(tr(localThermostatPower === false ? 'zones.resumeNow' : 'zones.resumeAutomation'))}</button></div>`
: '';
const localPowerDisabled = !device || device.enabled === false ? ' disabled' : '';
const localRequestedOn = localThermostatPower === true ? true : (localThermostatPower === false ? false : !!device?.power);
@@ -2223,4 +2245,5 @@ async function startApplication() {
if (location.pathname === '/' || location.pathname === `${APP_BASE}/`) updateBrowserUrl('/dashboard', true);
}
setInterval(updateLocalThermostatCountdowns, 1000);
startApplication().catch(error => console.error('Application startup failed:', error));
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'gree-controller-v0711-clean-local-thermostat';
const CACHE = 'gree-controller-v0712-timed-local-resume';
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')];