v0.13.4
This commit is contained in:
+9
-9
@@ -1189,16 +1189,16 @@
|
||||
<details class="profile-editor">
|
||||
<summary data-i18n="zones.profileTemperatures">Comfort / sleep / away temperatures</summary>
|
||||
<div class="profile-grid"><strong data-i18n="mode.cool">Cooling</strong><label><span
|
||||
data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.5"
|
||||
data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.1"
|
||||
name="cool_comfort_setpoint" value="23"></label><label><span data-i18n="preset.sleep">Sleep</span><input
|
||||
type="text" inputmode="decimal" step="0.5" name="cool_sleep_setpoint" value="24.5"></label><label><span
|
||||
data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5"
|
||||
type="text" inputmode="decimal" step="0.1" name="cool_sleep_setpoint" value="24.5"></label><label><span
|
||||
data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.1"
|
||||
name="cool_away_setpoint" value="27"></label></div>
|
||||
<div class="profile-grid"><strong data-i18n="mode.heat">Heating</strong><label><span
|
||||
data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.5"
|
||||
data-i18n="preset.comfort">Comfort</span><input type="text" inputmode="decimal" step="0.1"
|
||||
name="heat_comfort_setpoint" value="21"></label><label><span data-i18n="preset.sleep">Sleep</span><input
|
||||
type="text" inputmode="decimal" step="0.5" name="heat_sleep_setpoint" value="19"></label><label><span
|
||||
data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.5"
|
||||
type="text" inputmode="decimal" step="0.1" name="heat_sleep_setpoint" value="19"></label><label><span
|
||||
data-i18n="preset.away">Away</span><input type="text" inputmode="decimal" step="0.1"
|
||||
name="heat_away_setpoint" value="17"></label></div>
|
||||
</details>
|
||||
<div class="two zone-hysteresis-row">
|
||||
@@ -1280,8 +1280,8 @@
|
||||
<button type="button" class="danger" id="temporaryThermostatStop" data-i18n="zones.temporaryStop">Zakończ
|
||||
teraz</button>
|
||||
</div>
|
||||
<label><span data-i18n="zones.temporaryTarget">Temperatura szybkiego termostatu</span><input type="number"
|
||||
name="target_temperature" min="8" max="30" step="0.5" required></label>
|
||||
<label><span data-i18n="zones.temporaryTarget">Temperatura szybkiego termostatu</span><input type="text"
|
||||
inputmode="decimal" name="target_temperature" min="8" max="30" step="0.1" required></label>
|
||||
<label><span data-i18n="zones.temporaryStartRule">Kiedy uruchomić</span><select name="start_kind"
|
||||
id="temporaryStartKind">
|
||||
<option value="now" data-i18n="zones.temporaryStartNow">Od razu</option>
|
||||
@@ -1404,7 +1404,7 @@
|
||||
<option value="custom" data-i18n="preset.custom">Custom temperature</option>
|
||||
</select></label>
|
||||
<label id="scheduleSetpointField"><span data-i18n="common.temperatureC">Custom temperature °C</span><input
|
||||
type="text" inputmode="decimal" name="setpoint" step="0.5" min="8" max="30" value="23"></label>
|
||||
type="text" inputmode="decimal" name="setpoint" step="0.1" min="8" max="30" value="23"></label>
|
||||
<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
|
||||
|
||||
@@ -337,6 +337,7 @@ function zoneCard(zone, detailed = true) {
|
||||
const temporaryThermostatControl = temporarySession
|
||||
? `<button type="button" class="temporary-thermostat-toggle active ${temporaryStatus?.pending ? 'scheduled' : ''}" data-action="zone-open-temporary" data-id="${esc(zone.id)}" title="${esc(temporaryStatus?.detail || tr('zones.temporaryActive'))}"><b data-temporary-countdown-zone="${esc(zone.id)}">${esc(temporaryStatus?.countdown || '—')}</b></button>`
|
||||
: `<button type="button" class="temporary-thermostat-toggle" data-action="zone-open-temporary" data-id="${esc(zone.id)}" title="${esc(tr('zones.temporaryStartHint'))}"${localPowerDisabled}><b>${esc(tr('zones.temporaryShort'))}</b></button>`;
|
||||
const disabledManualControlNotice = zone.enabled ? '' : `<div class="manual-zone-warning zone-disabled-manual-note" role="note"><strong>${esc(tr('zones.disabledManualControlTitle'))}</strong><p>${esc(tr('zones.disabledManualControlHint'))}</p></div>`;
|
||||
|
||||
if (detailed) {
|
||||
const groupNames = (app.groups || []).filter(group => (group.zone_ids || []).includes(zone.id)).map(group => group.name);
|
||||
@@ -364,6 +365,7 @@ function zoneCard(zone, detailed = true) {
|
||||
const runtimeStatus = zoneRuntimeStatusLabel(zone, effectiveMode, device);
|
||||
return `<article class="list-card zone-thermostat quick-control-card quick-thermostat-control ${workingDemand ? 'demanding' : ''} ${waitingDemand ? 'lockout-waiting' : ''} ${zone.enabled ? '' : 'zone-disabled'} ${deviceManualOverride ? 'manual-takeover' : ''} ${controlGroup ? 'group-controlled' : ''}" data-zone-card="${esc(zone.id)}"${groupControlStyle(visualGroup)}>
|
||||
<div class="list-card-head"><div><h3>${esc(zone.name)}</h3><p>${esc(device?.name || tr('common.noDevice'))} · ${esc(zonePresetLabel(displayPreset))}</p></div><div class="zone-quick-actions">${localThermostatPowerControl}${temporaryThermostatControl}</div></div>
|
||||
${disabledManualControlNotice}
|
||||
<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 editable-target" data-temperature-kind="zone" data-id="${esc(zone.id)}" data-value="${Number.isFinite(target) ? target : ''}" tabindex="0" role="button" title="${esc(tr('common.clickTemperatureToEdit'))}">${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('')}
|
||||
|
||||
+2
-2
@@ -530,7 +530,7 @@ function renderFlowInspector() {
|
||||
else if (node.kind === 'logic_and') fields = `<p class="field-note">${esc(tr('flow.andHint'))}</p>`;
|
||||
else if (node.kind === 'logic_or') fields = `<p class="field-note">${esc(tr('flow.orHint'))}</p>`;
|
||||
else if (node.kind === 'logic_not') fields = `<p class="field-note">${esc(tr('flow.notHint'))}</p>`;
|
||||
else if (node.kind === 'zone_thermostat') fields = `<label><span>${esc(tr('common.zone'))}</span><select data-flow-config="zone_id">${flowSelectOptions(app.zones, c.zone_id)}</select></label><div class="two"><label><span>${esc(tr('schedules.profile'))}</span><select data-flow-config="preset">${['auto', 'comfort', 'sleep', 'away', 'custom'].map(v => `<option value="${v}" ${c.preset === v ? 'selected' : ''}>${esc(zonePresetLabel(v))}</option>`).join('')}</select></label><label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.5" data-flow-config="setpoint" value="${Number(c.setpoint ?? 21)}"></label></div><div class="two"><label><span>${esc(tr('common.mode'))}</span><select data-flow-config="mode"><option value="auto" ${c.mode === 'auto' ? 'selected' : ''}>Auto</option><option value="heat" ${c.mode === 'heat' ? 'selected' : ''}>${esc(tr('mode.heat'))}</option><option value="cool" ${c.mode === 'cool' ? 'selected' : ''}>${esc(tr('mode.cool'))}</option></select></label><label><span>${esc(tr('common.power'))}</span><select data-flow-config="power"><option value="" ${c.power == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option><option value="true" ${c.power === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${c.power === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label></div><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
||||
else if (node.kind === 'zone_thermostat') fields = `<label><span>${esc(tr('common.zone'))}</span><select data-flow-config="zone_id">${flowSelectOptions(app.zones, c.zone_id)}</select></label><div class="two"><label><span>${esc(tr('schedules.profile'))}</span><select data-flow-config="preset">${['auto', 'comfort', 'sleep', 'away', 'custom'].map(v => `<option value="${v}" ${c.preset === v ? 'selected' : ''}>${esc(zonePresetLabel(v))}</option>`).join('')}</select></label><label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.1" data-flow-config="setpoint" value="${Number(c.setpoint ?? 21)}"></label></div><div class="two"><label><span>${esc(tr('common.mode'))}</span><select data-flow-config="mode"><option value="auto" ${c.mode === 'auto' ? 'selected' : ''}>Auto</option><option value="heat" ${c.mode === 'heat' ? 'selected' : ''}>${esc(tr('mode.heat'))}</option><option value="cool" ${c.mode === 'cool' ? 'selected' : ''}>${esc(tr('mode.cool'))}</option></select></label><label><span>${esc(tr('common.power'))}</span><select data-flow-config="power"><option value="" ${c.power == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option><option value="true" ${c.power === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${c.power === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label></div><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
||||
else if (node.kind === 'device_action') fields = `<label><span>${esc(tr('common.device'))}</span><select data-flow-config="device_id">${flowSelectOptions(app.devices, c.device_id)}</select></label>${flowActionFields(c, false)}`;
|
||||
else if (node.kind === 'group_action') fields = `<label><span>${esc(tr('groups.group'))}</span><select data-flow-config="group_id">${flowSelectOptions(app.groups, c.group_id)}</select></label>${flowActionFields(c, true)}`;
|
||||
else if (node.kind === 'ha_service_action') fields = `<div class="two"><label><span>domain</span><input data-flow-config="domain" value="${esc(c.domain || 'switch')}" placeholder="switch"></label><label><span>service</span><input data-flow-config="service" value="${esc(c.service || 'turn_on')}" placeholder="turn_on"></label></div><p class="field-note">${esc(tr('flow.serviceExample'))}</p><label><span>entity_id</span><input data-flow-config="entity_id" value="${esc(c.entity_id || '')}" placeholder="switch.gas"></label><label><span>${esc(tr('flow.serviceData'))}</span><textarea rows="5" data-flow-config="data_json">${esc(JSON.stringify(c.data || {}, null, 2))}</textarea></label><label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
||||
@@ -547,7 +547,7 @@ function flowActionFields(c, group) {
|
||||
const modeOptions = modes.map(v => `<option value="${v}" ${c.mode === v ? 'selected' : ''}>${esc(v === 'auto' ? 'Auto' : v === 'house' ? tr('flow.houseMode') : (tr(`mode.${v}`) || v))}</option>`).join('');
|
||||
const base = `<div class="two"><label><span>${esc(tr('common.power'))}</span><select data-flow-config="power"><option value="" ${c.power == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option><option value="true" ${c.power === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${c.power === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label><label><span>${esc(tr('common.mode'))}</span><select data-flow-config="mode"><option value="">${esc(tr('actions.noChange'))}</option>${modeOptions}</select></label></div>`;
|
||||
const target = group
|
||||
? `<label><span>${esc(tr('groups.profile'))}</span><select data-flow-config="preset"><option value="">${esc(tr('actions.noChange'))}</option>${['auto', 'comfort', 'sleep', 'away', 'custom'].map(v => `<option value="${v}" ${c.preset === v ? 'selected' : ''}>${esc(zonePresetLabel(v))}</option>`).join('')}</select></label><label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.5" data-flow-config="setpoint" value="${c.setpoint ?? 21}"></label>`
|
||||
? `<label><span>${esc(tr('groups.profile'))}</span><select data-flow-config="preset"><option value="">${esc(tr('actions.noChange'))}</option>${['auto', 'comfort', 'sleep', 'away', 'custom'].map(v => `<option value="${v}" ${c.preset === v ? 'selected' : ''}>${esc(zonePresetLabel(v))}</option>`).join('')}</select></label><label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.1" data-flow-config="setpoint" value="${c.setpoint ?? 21}"></label>`
|
||||
: `<label><span>${esc(tr('common.targetC'))}</span><input type="number" min="8" max="30" step="0.5" data-flow-config="target_temperature" value="${c.target_temperature ?? ''}"></label>`;
|
||||
const deviceOptions = group ? '' : `<details class="flow-device-options"><summary>${esc(tr('flow.deviceOptions'))}</summary><div class="two"><label><span>fan_speed</span><select data-flow-config="fan_speed"><option value="" ${c.fan_speed == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option>${[0, 1, 2, 3, 4, 5].map(v => `<option value="${v}" ${Number(c.fan_speed) === v ? 'selected' : ''}>${v}</option>`).join('')}</select></label>${flowOptionalBoolField(c, 'swing_vertical')}${flowOptionalBoolField(c, 'swing_horizontal')}${flowOptionalBoolField(c, 'quiet')}${flowOptionalBoolField(c, 'turbo')}${flowOptionalBoolField(c, 'light')}${flowOptionalBoolField(c, 'air')}${flowOptionalBoolField(c, 'xfan')}${flowOptionalBoolField(c, 'health')}${flowOptionalBoolField(c, 'sleep')}</div><p class="field-note">${esc(tr('flow.deviceOptionsHint'))}</p></details>`;
|
||||
return `${base}${target}${deviceOptions}<label><span>${esc(tr('common.cooldownSeconds'))}</span><input type="number" min="30" data-flow-config="cooldown_seconds" value="${Number(c.cooldown_seconds || 60)}"></label>`;
|
||||
|
||||
@@ -126,6 +126,10 @@ function validateForm(form) {
|
||||
else if (!httpUrlValid(url.value)) { showFieldError(url, tr('validation.url')); valid = false; }
|
||||
}
|
||||
|
||||
if (form.id === 'temporaryThermostatForm') {
|
||||
if (!validateDecimalField(form, 'target_temperature', 8, 30)) valid = false;
|
||||
}
|
||||
|
||||
if (form.id === 'zoneForm') {
|
||||
const source = form.elements.sensor_source?.value;
|
||||
const entity = form.elements.ha_entity_id;
|
||||
|
||||
+5
-4
@@ -750,11 +750,12 @@ h3 {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.zone-thermostat.zone-disabled .thermostat-main,
|
||||
.zone-thermostat.zone-disabled .preset-row,
|
||||
.zone-thermostat.zone-disabled .zone-mode-row,
|
||||
.zone-thermostat.zone-disabled .zone-state-line {
|
||||
opacity: .5;
|
||||
opacity: .68;
|
||||
}
|
||||
|
||||
.zone-disabled-manual-note {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-stats {
|
||||
|
||||
Reference in New Issue
Block a user