v0.9.13
This commit is contained in:
+18
-5
@@ -78,15 +78,17 @@ function planZoneEffectiveEnabled(zone) {
|
||||
|
||||
function renderControlPlan() {
|
||||
const host = $('#controlPlan'); if (!host) return;
|
||||
const section = $('#controlPlanSection');
|
||||
const plan = app.controlPlan;
|
||||
if (!plan) {
|
||||
if (section) section.hidden = false;
|
||||
host.innerHTML = `<div class="panel plan-loading">${esc(tr('plan.loading'))}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const allZones = plan.zones || [];
|
||||
const houseEvents = (plan.next_events || []).slice(0, 3);
|
||||
const house = `<article class="panel plan-card plan-house"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.house'))}</span><h3>${esc(houseModeLabel(plan.house_mode || 'off'))}</h3></div><span class="badge active">${esc((plan.control_strategy || 'setpoint') === 'setpoint' ? tr('plan.strategySetpoint') : (plan.control_strategy || 'setpoint'))}</span></div><p>${esc(tr('plan.houseSummary', { zones: allZones.filter(planZoneEffectiveEnabled).length, demand: allZones.filter(zone => planZoneEffectiveEnabled(zone) && zone.demand).length }))}</p><ul class="plan-events">${houseEvents.length ? houseEvents.map(planEventMarkup).join('') : `<li class="muted">${esc(tr('plan.noEvents'))}</li>`}</ul></article>`;
|
||||
const house = houseEvents.length ? `<article class="panel plan-card plan-house"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.house'))}</span><h3>${esc(houseModeLabel(plan.house_mode || 'off'))}</h3></div><span class="badge active">${esc((plan.control_strategy || 'setpoint') === 'setpoint' ? tr('plan.strategySetpoint') : (plan.control_strategy || 'setpoint'))}</span></div><p>${esc(tr('plan.houseSummary', { zones: allZones.filter(planZoneEffectiveEnabled).length, demand: allZones.filter(zone => planZoneEffectiveEnabled(zone) && zone.demand).length }))}</p><ul class="plan-events">${houseEvents.map(planEventMarkup).join('')}</ul></article>` : '';
|
||||
|
||||
const groupCards = (app.groups || []).map(group => {
|
||||
const memberIds = new Set(group.zone_ids || []);
|
||||
@@ -98,7 +100,8 @@ function renderControlPlan() {
|
||||
const preset = state.preset === 'mixed' ? tr('groups.mixed') : zonePresetLabel(state.preset);
|
||||
const events = members.flatMap(zone => (zone.next_events || []).map(event => ({ ...event, label: `${zone.zone_name}: ${event.label}` })))
|
||||
.sort((a, b) => new Date(a.at) - new Date(b.at)).slice(0, 2);
|
||||
return `<article class="panel plan-card plan-group ${powerEnabled ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('groups.group'))}</span><h3>${esc(group.name)}</h3></div><span class="badge ${powerEnabled ? 'active' : ''}">${esc(powerEnabled ? tr('common.on') : tr('common.off'))}</span></div><div class="plan-group-state"><strong>${esc(mode)}</strong><span>·</span><strong>${esc(preset)}</strong></div><p>${esc(tr('plan.groupSummary', { zones: members.length, demand }))}</p><ul class="plan-events">${events.length ? events.map(planEventMarkup).join('') : `<li class="muted">${esc(tr(powerEnabled ? 'plan.noEvents' : 'plan.groupOff'))}</li>`}</ul></article>`;
|
||||
if (!events.length) return '';
|
||||
return `<article class="panel plan-card plan-group ${powerEnabled ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('groups.group'))}</span><h3>${esc(group.name)}</h3></div><span class="badge ${powerEnabled ? 'active' : ''}">${esc(powerEnabled ? tr('common.on') : tr('common.off'))}</span></div><div class="plan-group-state"><strong>${esc(mode)}</strong><span>·</span><strong>${esc(preset)}</strong></div><p>${esc(tr('plan.groupSummary', { zones: members.length, demand }))}</p><ul class="plan-events">${events.map(planEventMarkup).join('')}</ul></article>`;
|
||||
}).join('');
|
||||
|
||||
const zoneGroups = new Map();
|
||||
@@ -109,16 +112,26 @@ function renderControlPlan() {
|
||||
}));
|
||||
const zones = allZones.map(zone => {
|
||||
const events = (zone.next_events || []).slice(0, 2);
|
||||
if (!events.length) return '';
|
||||
const target = zone.target_temperature == null ? '--' : Number(zone.target_temperature).toFixed(1);
|
||||
const groupNames = zoneGroups.get(zone.zone_id) || [];
|
||||
const scope = groupNames.length ? `${tr('groups.group')}: ${groupNames.join(' · ')}` : (zone.device_name || tr('common.noDevice'));
|
||||
const planState = zoneRuntimeStatusLabel(zone, zone.mode || 'off');
|
||||
return `<article class="panel plan-card plan-zone ${planZoneEffectiveEnabled(zone) ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(scope)}</span><h3>${esc(zone.zone_name)}</h3></div><span class="badge ${zone.device_manual_override ? 'manual-override' : (planZoneEffectiveEnabled(zone) && zone.demand ? 'active' : '')}">${esc(planState)}</span></div><div class="plan-temp"><span>${fmtTemp(zone.current_temperature)}</span><b>→</b><strong>${esc(target)}<small>°C</small></strong></div><p>${esc(houseModeLabel(zone.mode || 'off'))} · ${esc(zonePresetLabel(zone.preset))}${zone.current_schedule_name ? ` · ${esc(zone.current_schedule_name)}` : ''}</p><ul class="plan-events">${events.length ? events.map(planEventMarkup).join('') : `<li class="muted">${esc(tr('plan.noEvents'))}</li>`}</ul></article>`;
|
||||
return `<article class="panel plan-card plan-zone ${planZoneEffectiveEnabled(zone) ? '' : 'disabled'}"><div class="plan-card-head"><div><span class="eyebrow">${esc(scope)}</span><h3>${esc(zone.zone_name)}</h3></div><span class="badge ${zone.device_manual_override ? 'manual-override' : (planZoneEffectiveEnabled(zone) && zone.demand ? 'active' : '')}">${esc(planState)}</span></div><div class="plan-temp"><span>${fmtTemp(zone.current_temperature)}</span><b>→</b><strong>${esc(target)}<small>°C</small></strong></div><p>${esc(houseModeLabel(zone.mode || 'off'))} · ${esc(zonePresetLabel(zone.preset))}${zone.current_schedule_name ? ` · ${esc(zone.current_schedule_name)}` : ''}</p><ul class="plan-events">${events.map(planEventMarkup).join('')}</ul></article>`;
|
||||
}).join('');
|
||||
|
||||
const rules = (plan.rules || []).filter(rule => rule.enabled);
|
||||
const ruleCard = rules.length ? `<article class="panel plan-card plan-rules"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.rules'))}</span><h3>${esc(tr('plan.ruleCount', { count: rules.length }))}</h3></div></div><ul class="plan-events">${rules.slice(0, 3).map(rule => `<li><time>${esc(automationTriggerLabel(rule))}</time><span>${esc(rule.name)} → ${esc(rule.action_group_name ? `${tr('groups.group')}: ${rule.action_group_name}` : (rule.action_device_name || ''))}</span></li>`).join('')}</ul></article>` : '';
|
||||
host.innerHTML = house + groupCards + zones + ruleCard;
|
||||
const ruleCard = rules.length ? `<article class="panel plan-card plan-rules"><div class="plan-card-head"><div><span class="eyebrow">${esc(tr('plan.rules'))}</span><h3>${esc(tr('plan.ruleCount', { count: rules.length }))}</h3></div></div><ul class="plan-events">${rules.slice(0, 3).map(rule => {
|
||||
const automation = (app.automations || []).find(item => item.id === rule.id);
|
||||
const flow = automation?.flow_id ? (app.flows || []).find(item => item.id === automation.flow_id) : null;
|
||||
const displayName = flow?.name || rule.name;
|
||||
const target = rule.action_group_name ? `${tr('groups.group')}: ${rule.action_group_name}` : (rule.action_device_name || '');
|
||||
return `<li><time>${esc(automationTriggerLabel(rule))}</time><span class="plan-rule-summary"><strong>${esc(displayName)}</strong>${target ? `<small>→ ${esc(target)}</small>` : ''}</span></li>`;
|
||||
}).join('')}</ul></article>` : '';
|
||||
|
||||
const content = house + groupCards + zones + ruleCard;
|
||||
host.innerHTML = content;
|
||||
if (section) section.hidden = !content;
|
||||
}
|
||||
async function loadControlPlan() {
|
||||
try { app.controlPlan = await api('/api/control-plan'); renderControlPlan(); renderSimulationPage(); }
|
||||
|
||||
Reference in New Issue
Block a user