v0.8.0
This commit is contained in:
+25
-3
@@ -708,6 +708,27 @@ function zonePresetLabel(preset) {
|
||||
return tr(key) === key ? (preset || 'comfort') : tr(key);
|
||||
}
|
||||
|
||||
function zoneControlOwnerLabel(zone) {
|
||||
const owner=zone.control_owner || (zone.device_manual_override ? 'direct_manual' : (zone.local_thermostat_power != null ? 'local_thermostat' : 'automation'));
|
||||
const source=zone.control_source || '';
|
||||
if(owner==='global_off') return tr('zones.ownerGlobalOff');
|
||||
if(owner==='direct_manual') {
|
||||
if(source==='home_assistant_direct') return tr('zones.ownerHaDirect');
|
||||
if(source==='web_direct') return tr('zones.ownerWebDirect');
|
||||
return tr('zones.ownerExternal');
|
||||
}
|
||||
if(owner==='local_thermostat') return tr('zones.ownerLocalThermostat');
|
||||
return tr('zones.ownerAutomation');
|
||||
}
|
||||
|
||||
function zoneControlOwnerMeta(zone) {
|
||||
const parts=[];
|
||||
if(zone.control_since) parts.push(`${tr('zones.ownerSince')} ${new Date(zone.control_since).toLocaleTimeString(locale(),{hour:'2-digit',minute:'2-digit'})}`);
|
||||
if(zone.control_resume_at) parts.push(`${tr('zones.ownerResume')} ${new Date(zone.control_resume_at).toLocaleTimeString(locale(),{hour:'2-digit',minute:'2-digit'})}`);
|
||||
if(zone.lockout_until && new Date(zone.lockout_until)>new Date()) parts.push(`${tr('zones.lockoutUntil')} ${new Date(zone.lockout_until).toLocaleTimeString(locale(),{hour:'2-digit',minute:'2-digit'})}`);
|
||||
return parts.join(' · ');
|
||||
}
|
||||
|
||||
function zoneRuntimeStatusLabel(zone, effectiveMode) {
|
||||
if (zone.device_manual_override === true) return tr('zones.manualDeviceControl');
|
||||
if (!zone.enabled) return tr('common.disabled');
|
||||
@@ -764,7 +785,7 @@ function zoneCard(zone, detailed = true) {
|
||||
<div class="list-card-head"><div><span class="eyebrow">${esc(tr('zones.configuration'))}</span><h3>${esc(zone.name)}</h3><p>${esc(device?.name || tr('common.noDevice'))} · ${esc(tr('groups.group'))}: ${esc(groupText)}</p></div><span class="badge ${zone.enabled ? 'active' : ''}">${esc(state)}</span></div>
|
||||
<div class="zone-config-status">
|
||||
<div class="zone-config-temperature"><small>${esc(tr('zones.currentStatus'))}</small><div><span>${fmtTemp(roomTemperature)}</span><b>→</b><strong>${Number.isFinite(target) ? `${target.toFixed(1)}°C` : '—'}</strong></div></div>
|
||||
<div class="zone-config-runtime"><span class="badge ${zone.enabled && zone.demand ? 'active' : ''}">${esc(zoneRuntimeStatusLabel(zone, effectiveMode))}</span><span>${esc(houseModeLabel(effectiveMode))} · ${esc(zonePresetLabel(displayPreset))}</span><small>${esc(override)}</small></div>
|
||||
<div class="zone-config-runtime"><span class="badge ${zone.enabled && zone.demand ? 'active' : ''}">${esc(zoneRuntimeStatusLabel(zone, effectiveMode))}</span><span>${esc(houseModeLabel(effectiveMode))} · ${esc(zonePresetLabel(displayPreset))}</span><small>${esc(override)}</small><small><strong>${esc(tr('zones.controlOwner'))}:</strong> ${esc(zoneControlOwnerLabel(zone))}${zoneControlOwnerMeta(zone) ? ` · ${esc(zoneControlOwnerMeta(zone))}` : ''}</small></div>
|
||||
</div>
|
||||
<div class="zone-config-grid">
|
||||
<div><small>${esc(tr('zones.modePolicy'))}</small><strong>${esc(policy)}</strong></div>
|
||||
@@ -787,6 +808,7 @@ function zoneCard(zone, detailed = true) {
|
||||
</div>
|
||||
<div class="mode-row zone-mode-row quick-control-row quick-control-row-3"><button class="${mode==='house'&&globalModeAvailable?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="house"${globalModeDisabled}>${esc(tr('zones.followHouseShort'))}</button><button class="${mode==='heat'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="heat">${esc(modeLabel('heat'))}</button><button class="${mode==='cool'?'active':''}" data-action="zone-mode" data-id="${esc(zone.id)}" data-value="cool">${esc(modeLabel('cool'))}</button></div>
|
||||
<div class="zone-state-line"><span>${esc(zoneRuntimeStatusLabel(zone, effectiveMode))}</span><span>${esc(override)}</span></div>
|
||||
<div class="zone-state-line control-owner-line"><span><strong>${esc(tr('zones.controlOwner'))}:</strong> ${esc(zoneControlOwnerLabel(zone))}</span><span>${esc(zoneControlOwnerMeta(zone))}</span></div>
|
||||
${manualTakeover}
|
||||
</article>`;
|
||||
}
|
||||
@@ -1881,7 +1903,7 @@ $('#copyZoneSettings')?.addEventListener('click',()=>{
|
||||
ha_entity_id:f.elements.ha_entity_id.value,
|
||||
enabled:f.elements.enabled.checked,
|
||||
};
|
||||
const copyFields=['setpoint','cool_comfort_setpoint','cool_sleep_setpoint','cool_away_setpoint','heat_comfort_setpoint','heat_sleep_setpoint','heat_away_setpoint','hysteresis','min_on_seconds','min_off_seconds','min_adjust_seconds','standby_offset_c','external_sensor_weight_percent','max_sensor_difference'];
|
||||
const copyFields=['setpoint','cool_comfort_setpoint','cool_sleep_setpoint','cool_away_setpoint','heat_comfort_setpoint','heat_sleep_setpoint','heat_away_setpoint','hysteresis','min_on_seconds','min_off_seconds','min_adjust_seconds','standby_offset_c','external_sensor_weight_percent','max_sensor_difference','sensor_stale_after_seconds'];
|
||||
copyFields.forEach(name=>{
|
||||
const input=f.elements[name];
|
||||
if(!input)return;
|
||||
@@ -1955,7 +1977,7 @@ $('#zoneForm').addEventListener('submit', async event => {
|
||||
heat_comfort_setpoint:parseDecimal(raw.heat_comfort_setpoint),heat_sleep_setpoint:parseDecimal(raw.heat_sleep_setpoint),heat_away_setpoint:parseDecimal(raw.heat_away_setpoint),
|
||||
hysteresis:parseDecimal(raw.hysteresis),min_on_seconds:Number(raw.min_on_seconds),min_off_seconds:Number(raw.min_off_seconds),
|
||||
min_adjust_seconds:Number(raw.min_adjust_seconds),standby_offset_c:parseDecimal(raw.standby_offset_c),smart_fan:form.smart_fan.checked,
|
||||
sensor_source:raw.sensor_source,ha_entity_id:raw.ha_entity_id||null,external_sensor_weight:Number(raw.external_sensor_weight_percent)/100,max_sensor_difference:parseDecimal(raw.max_sensor_difference)
|
||||
sensor_source:raw.sensor_source,ha_entity_id:raw.ha_entity_id||null,external_sensor_weight:Number(raw.external_sensor_weight_percent)/100,max_sensor_difference:parseDecimal(raw.max_sensor_difference),sensor_stale_after_seconds:Number(raw.sensor_stale_after_seconds||300),revision:raw.revision?Number(raw.revision):null
|
||||
};
|
||||
try { await api(id?`/api/zones/${encodeURIComponent(id)}`:'/api/zones',{method:id?'PUT':'POST',body}); form.closest('dialog').close(); form.reset(); await loadBootstrap(); toast(tr('common.saved')); }
|
||||
catch(error){toast(error.message,true);}
|
||||
|
||||
+1
-1
@@ -394,7 +394,7 @@
|
||||
</div>
|
||||
<div class="two"><label><span data-i18n="zones.minAdjust">Minimum adjustment interval (s)</span><input type="number" min="15" name="min_adjust_seconds" value="120"></label><label><span data-i18n="zones.standbyOffset">Standby setpoint offset °C</span><input type="text" inputmode="decimal" step="0.5" min="0.5" max="8" name="standby_offset_c" value="2"></label></div>
|
||||
<label class="check"><input type="checkbox" name="smart_fan" checked> <span data-i18n="zones.smartFan">Smart fan assist</span></label>
|
||||
<input type="hidden" name="min_on_seconds" value="180"><input type="hidden" name="min_off_seconds" value="180">
|
||||
<input type="hidden" name="min_on_seconds" value="180"><input type="hidden" name="min_off_seconds" value="180"><input type="hidden" name="sensor_stale_after_seconds" value="300"><input type="hidden" name="revision" value="">
|
||||
<label class="check"><input type="checkbox" name="enabled" checked> <span data-i18n="common.enabled">Enabled</span></label>
|
||||
<div class="form-actions"><button type="button" class="secondary" data-close data-i18n="actions.cancel">Cancel</button><button class="primary" type="submit" data-i18n="actions.save">Save</button></div>
|
||||
</form>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'gree-controller-v0713-race-frame-counters';
|
||||
const CACHE = 'gree-controller-v080-control-ownership';
|
||||
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')];
|
||||
|
||||
Reference in New Issue
Block a user