v0.15.2
This commit is contained in:
+24
-1
@@ -982,7 +982,8 @@
|
||||
<div class="flow-palette-group block-category-action"><span data-i18n="flow.actions">Akcje</span>
|
||||
<button type="button" data-flow-add="zone_thermostat"
|
||||
data-i18n="flow.node.thermostat">Termostat</button><button type="button" data-flow-add="device_action"
|
||||
data-i18n="flow.node.greeDevice">Urządzenie GREE</button><button type="button" data-flow-add="group_action"
|
||||
data-i18n="flow.node.greeDevice">Urządzenie GREE</button><button type="button" data-flow-add="device_feature_action"
|
||||
data-i18n="flow.node.greeFeature">Pojedyncza funkcja GREE</button><button type="button" data-flow-add="group_action"
|
||||
data-i18n="flow.node.group">Grupa</button>
|
||||
</div>
|
||||
<div class="flow-palette-group block-category-haaction"><span data-i18n="flow.haActions">Home Assistant</span>
|
||||
@@ -1786,6 +1787,28 @@
|
||||
<option value="6" data-i18n="louver.horizontal.fixedRight">Fixed: Right</option>
|
||||
</select></label>
|
||||
</div>
|
||||
<details id="automationDeviceFeatureOptions" class="flow-device-options">
|
||||
<summary data-i18n="automations.singleDeviceFunctions">Single device functions</summary>
|
||||
<div class="two">
|
||||
<label><span data-i18n="common.fan">Fan</span><select name="action_fan_speed">
|
||||
<option value="" data-i18n="actions.noChange">No change</option>
|
||||
<option value="0" data-i18n="fan.auto">Auto</option>
|
||||
<option value="1" data-i18n="fan.low">Low</option>
|
||||
<option value="2" data-i18n="fan.mediumLow">Medium-low</option>
|
||||
<option value="3" data-i18n="fan.medium">Medium</option>
|
||||
<option value="4" data-i18n="fan.mediumHigh">Medium-high</option>
|
||||
<option value="5" data-i18n="fan.high">High</option>
|
||||
</select></label>
|
||||
<label><span data-i18n="devices.quiet">Quiet</span><select name="action_quiet"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
<label><span data-i18n="devices.turbo">Turbo</span><select name="action_turbo"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
<label><span data-i18n="devices.light">Panel light</span><select name="action_light"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
<label><span data-i18n="devices.air">Air</span><select name="action_air"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
<label><span data-i18n="devices.xfan">X-Fan</span><select name="action_xfan"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
<label><span data-i18n="devices.health">Health</span><select name="action_health"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
<label><span data-i18n="devices.sleep">Sleep</span><select name="action_sleep"><option value="" data-i18n="actions.noChange">No change</option><option value="true" data-i18n="common.on">On</option><option value="false" data-i18n="common.off">Off</option></select></label>
|
||||
</div>
|
||||
<p class="field-note" data-i18n="automations.singleDeviceFunctionsHint">Leave every other field as No change to modify only the selected unit function.</p>
|
||||
</details>
|
||||
<p class="field-note" id="automationGroupHint" hidden data-i18n="automations.groupHint">For a group, Auto mode
|
||||
means follow the whole-house heating/cooling mode. Group presets are applied to all member zones.</p>
|
||||
<label class="check"><input type="checkbox" name="enabled" checked> <span
|
||||
|
||||
@@ -115,6 +115,9 @@ const houseModeLabel = mode => mode === 'off' ? tr('house.noControl') : modeLabe
|
||||
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 deviceCommandFieldLabel = key => {
|
||||
const translationKey = {
|
||||
power: 'common.power',
|
||||
mode: 'common.mode',
|
||||
target_temperature: 'common.targetC',
|
||||
fan_speed: 'common.fan',
|
||||
swing_vertical: 'devices.swingVertical',
|
||||
swing_horizontal: 'devices.swingHorizontal',
|
||||
|
||||
@@ -712,6 +712,18 @@ function renderSchedules() {
|
||||
}).join('') : `<div class="empty"><strong>${esc(tr('schedules.emptyTitle'))}</strong>${esc(tr('schedules.emptyText'))}</div>`;
|
||||
}
|
||||
|
||||
function automationDeviceFunctionSummary(action = {}) {
|
||||
const parts = [];
|
||||
if (action.target_temperature != null) parts.push(fmtTemp(action.target_temperature));
|
||||
if (action.fan_speed != null) parts.push(`${deviceCommandFieldLabel('fan_speed')}: ${fanLabel(action.fan_speed)}`);
|
||||
if (action.swing_vertical != null) parts.push(`${deviceCommandFieldLabel('swing_vertical')}: ${louverPositionLabel('vertical', action.swing_vertical)}`);
|
||||
if (action.swing_horizontal != null) parts.push(`${deviceCommandFieldLabel('swing_horizontal')}: ${louverPositionLabel('horizontal', action.swing_horizontal)}`);
|
||||
['quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep'].forEach(key => {
|
||||
if (action[key] != null) parts.push(`${deviceCommandFieldLabel(key)}: ${action[key] ? tr('common.on') : tr('common.off')}`);
|
||||
});
|
||||
return parts.join(' · ') || '—';
|
||||
}
|
||||
|
||||
function renderAutomations() {
|
||||
$('#automationList').innerHTML = app.automations.length ? app.automations.map(item => {
|
||||
const actionZone = item.action_zone_id ? app.zones.find(zone => zone.id === item.action_zone_id) : null;
|
||||
@@ -720,8 +732,10 @@ function renderAutomations() {
|
||||
const targetName = actionZone?.name || actionGroup?.name || actionDevice?.name || tr('common.noDevice');
|
||||
const mode = item.action.mode === 'auto' && (actionGroup || actionZone) ? tr('groups.followHouse') : (item.action.mode ? modeLabel(item.action.mode) : '—');
|
||||
const preset = item.action_zone_preset ? zonePresetLabel(item.action_zone_preset) : (item.action_preset ? zonePresetLabel(item.action_preset) : '—');
|
||||
const deviceFunction = actionZone || actionGroup ? preset : automationDeviceFunctionSummary(item.action);
|
||||
const deviceFunctionLabel = actionZone || actionGroup ? tr('groups.profile') : tr('automations.deviceFunction');
|
||||
return `<article class="list-card ${item.flow_id ? 'flow-generated-card' : ''}"><div class="list-card-head"><div><h3>${esc(item.name)}</h3><p>${esc(tr('automations.triggerSummary', { trigger: automationTriggerLabel(item), device: targetName }))}</p></div><span class="badge ${item.enabled ? 'active' : ''}">${esc(item.enabled ? tr('common.active') : tr('common.disabled'))}</span></div>
|
||||
<div class="card-stats"><div class="card-stat"><small>${esc(tr('common.power'))}</small><strong>${item.action.power == null ? '—' : item.action.power ? tr('common.on') : tr('common.off')}</strong></div><div class="card-stat"><small>${esc(tr('common.mode'))}</small><strong>${esc(mode)}</strong></div><div class="card-stat"><small>${esc(tr('groups.profile'))}</small><strong>${esc(preset)}</strong></div><div class="card-stat"><small>${esc(tr('automations.last'))}</small><strong>${item.last_fired_at ? new Date(item.last_fired_at).toLocaleTimeString(locale(), { hour: '2-digit', minute: '2-digit' }) : '—'}</strong></div></div>
|
||||
<div class="card-stats"><div class="card-stat"><small>${esc(tr('common.power'))}</small><strong>${item.action.power == null ? '—' : item.action.power ? tr('common.on') : tr('common.off')}</strong></div><div class="card-stat"><small>${esc(tr('common.mode'))}</small><strong>${esc(mode)}</strong></div><div class="card-stat"><small>${esc(deviceFunctionLabel)}</small><strong>${esc(deviceFunction)}</strong></div><div class="card-stat"><small>${esc(tr('automations.last'))}</small><strong>${item.last_fired_at ? new Date(item.last_fired_at).toLocaleTimeString(locale(), { hour: '2-digit', minute: '2-digit' }) : '—'}</strong></div></div>
|
||||
<div class="card-footer"><small>${esc(item.flow_id ? tr('flow.generatedReadOnly') : (actionGroup ? `${tr('groups.group')}: ${targetName}` : `${tr('common.device')}: ${targetName}`))} · ${esc(tr('common.cooldown'))} ${item.cooldown_seconds}s</small><div class="card-menu">${item.flow_id ? `<button data-action="edit-flow" data-id="${esc(item.flow_id)}">${esc(tr('flow.openEditor'))}</button>` : `<button data-action="edit-automation" data-id="${esc(item.id)}">${esc(tr('actions.edit'))}</button><details class="card-overflow"><summary aria-label="${esc(tr('actions.more'))}" title="${esc(tr('actions.more'))}">${uiIcon('more-horizontal')}</summary><div class="card-overflow-menu"><button class="danger" data-action="delete-automation" data-id="${esc(item.id)}">${esc(tr('actions.delete'))}</button></div></details>`}</div></div></article>`;
|
||||
}).join('') : `<div class="empty"><strong>${esc(tr('automations.emptyTitle'))}</strong>${esc(tr('automations.emptyText'))}</div>`;
|
||||
}
|
||||
|
||||
@@ -643,7 +643,7 @@ $('#scheduleForm').addEventListener('submit', async event => {
|
||||
$('#automationForm').addEventListener('submit', async event => {
|
||||
event.preventDefault(); const form = event.currentTarget, raw = Object.fromEntries(new FormData(form)); const id = raw.id;
|
||||
const groupTarget = raw.action_target_kind === 'group';
|
||||
const action = {}; if (raw.action_power !== '') action.power = raw.action_power === 'true'; if (raw.action_mode) action.mode = raw.action_mode; if (!groupTarget && raw.action_target_temperature !== '') action.target_temperature = parseDecimal(raw.action_target_temperature); if (!groupTarget && raw.action_swing_vertical !== '') action.swing_vertical = Number(raw.action_swing_vertical); if (!groupTarget && raw.action_swing_horizontal !== '') action.swing_horizontal = Number(raw.action_swing_horizontal);
|
||||
const action = {}; if (raw.action_power !== '') action.power = raw.action_power === 'true'; if (raw.action_mode) action.mode = raw.action_mode; if (!groupTarget && raw.action_target_temperature !== '') action.target_temperature = parseDecimal(raw.action_target_temperature); if (!groupTarget && raw.action_fan_speed !== '') action.fan_speed = Number(raw.action_fan_speed); if (!groupTarget && raw.action_swing_vertical !== '') action.swing_vertical = Number(raw.action_swing_vertical); if (!groupTarget && raw.action_swing_horizontal !== '') action.swing_horizontal = Number(raw.action_swing_horizontal); if (!groupTarget) ['quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep'].forEach(key => { const value = raw[`action_${key}`]; if (value !== '') action[key] = value === 'true'; });
|
||||
const body = { name: raw.name, enabled: form.enabled.checked, trigger_kind: raw.trigger_kind, trigger_device_id: raw.trigger_device_id || null, threshold: raw.threshold === '' ? null : parseDecimal(raw.threshold), at_time: raw.at_time || null, action_device_id: groupTarget ? '' : raw.action_device_id, action_group_id: groupTarget ? (raw.action_group_id || null) : null, action_preset: groupTarget ? (raw.action_preset || null) : null, action, cooldown_seconds: Number(raw.cooldown_seconds) };
|
||||
await runFormTask(form, async () => {
|
||||
await api(id ? `/api/automations/${encodeURIComponent(id)}` : '/api/automations', { method: id ? 'PUT' : 'POST', body }); form.closest('dialog').close(); form.reset(); await loadBootstrap(); toast(tr('common.saved'));
|
||||
|
||||
+45
-1
@@ -29,6 +29,7 @@ const FLOW_NODE_META = Object.freeze({
|
||||
logic_not: { titleKey: 'flow.node.not', category: 'logic' },
|
||||
zone_thermostat: { titleKey: 'flow.node.thermostat', category: 'action' },
|
||||
device_action: { titleKey: 'flow.node.greeDevice', category: 'action' },
|
||||
device_feature_action: { titleKey: 'flow.node.greeFeature', category: 'action' },
|
||||
group_action: { titleKey: 'flow.node.group', category: 'action' },
|
||||
ha_service_action: { titleKey: 'flow.node.haServiceAction', category: 'haaction' },
|
||||
});
|
||||
@@ -97,6 +98,7 @@ function flowDefaultConfig(kind) {
|
||||
if (kind === 'shared_input') return sharedFlowReferenceDefaultConfig(app.flowSharedInputs?.[0]);
|
||||
if (kind === 'zone_thermostat') return { zone_id: app.zones[0]?.id || '', preset: 'comfort', setpoint: 21, mode: 'auto', power: null, swing_vertical: null, swing_horizontal: null, cooldown_seconds: 60 };
|
||||
if (kind === 'device_action') return { device_id: app.devices[0]?.id || '', power: true, mode: '', target_temperature: null, fan_speed: null, swing_vertical: null, swing_horizontal: null, quiet: null, turbo: null, light: null, air: null, xfan: null, health: null, sleep: null, cooldown_seconds: 60 };
|
||||
if (kind === 'device_feature_action') return { device_id: app.devices[0]?.id || '', feature: 'light', value: true, cooldown_seconds: 60 };
|
||||
if (kind === 'group_action') return { group_id: app.groups[0]?.id || '', power: true, mode: '', preset: '', setpoint: 21, cooldown_seconds: 60 };
|
||||
if (kind === 'ha_service_action') return { domain: 'switch', service: 'turn_on', entity_id: '', data: {}, cooldown_seconds: 60 };
|
||||
return {};
|
||||
@@ -357,6 +359,7 @@ function flowNodeSummary(node) {
|
||||
if (node.kind === 'logic_not') return tr('flow.invertCondition');
|
||||
if (node.kind === 'zone_thermostat') { const zone = app.zones.find(z => z.id === c.zone_id); return `${zone?.name || tr('common.noZone')} · ${c.preset === 'custom' ? fmtTemp(c.setpoint) : zonePresetLabel(c.preset || 'comfort')}`; }
|
||||
if (node.kind === 'device_action') { const d = app.devices.find(v => v.id === c.device_id); return `${d?.name || tr('common.noDevice')} · ${c.power == null ? tr('actions.noChange') : c.power ? tr('common.on') : tr('common.off')}`; }
|
||||
if (node.kind === 'device_feature_action') { const d = app.devices.find(v => v.id === c.device_id); return `${d?.name || tr('common.noDevice')} · ${deviceCommandFieldLabel(c.feature || 'light')} → ${flowDeviceFeatureValueLabel(c.feature, c.value)}`; }
|
||||
if (node.kind === 'group_action') { const g = app.groups.find(v => v.id === c.group_id); return `${g?.name || tr('groups.group')} · ${c.power == null ? tr('actions.noChange') : c.power ? tr('common.on') : tr('common.off')}`; }
|
||||
if (node.kind === 'ha_service_action') return `${c.domain || 'domain'}.${c.service || 'service'} · ${c.entity_id || 'entity_id'}`;
|
||||
return '';
|
||||
@@ -550,6 +553,7 @@ function renderFlowInspector() {
|
||||
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.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><details class="flow-device-options"><summary>${esc(tr('flow.thermostatDeviceOptions'))}</summary><div class="two">${flowOptionalLouverField(c, 'swing_vertical', 'vertical')}${flowOptionalLouverField(c, 'swing_horizontal', 'horizontal')}</div><p class="field-note">${esc(tr('flow.thermostatDeviceOptionsHint'))}</p></details><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 === 'device_feature_action') fields = `<label><span>${esc(tr('common.device'))}</span><select data-flow-config="device_id">${flowSelectOptions(app.devices, c.device_id)}</select></label><label><span>${esc(tr('flow.deviceFeature'))}</span><select data-flow-config="feature">${flowDeviceFeatureOptions(c.feature)}</select></label>${flowDeviceFeatureValueField(c)}<p class="field-note">${esc(tr('flow.deviceFeatureHint'))}</p><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 === '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" list="haEntitySuggestions" 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>`;
|
||||
host.innerHTML = `<div class="flow-inspector-head"><div><span class="eyebrow">${esc(tr('flow.blockSettings'))}</span><h3>${esc(flowNodeTitle(meta))}</h3></div><div class="flow-inspector-head-actions"><button type="button" class="secondary flow-inspector-expand" data-action="flow-toggle-inspector" aria-label="${esc(tr('flow.expandSettings'))}">${uiIcon('expand-vertical')}</button><button type="button" class="secondary flow-inspector-close" data-action="flow-clear-selection" aria-label="${esc(tr('flow.clearSelection'))}">${uiIcon('close')}</button><button type="button" class="danger flow-inspector-delete" data-flow-remove="${esc(node.id)}">${esc(tr('actions.delete'))}</button></div></div>${fields}<div class="flow-inspector-meta"><small>ID</small><code>${esc(node.id)}</code></div>`;
|
||||
@@ -562,6 +566,40 @@ function flowLouverComparisonFields(c, axis) {
|
||||
return `<div class="two"><label><span>${esc(tr('flow.operator'))}</span><select data-flow-config="operator">${options}</select></label><label><span>${esc(tr('flow.value'))}</span><select data-flow-config="value">${louverOptions(axis, value)}</select></label></div>`;
|
||||
}
|
||||
function flowComparisonFields(c, temperature = true) { return `<div class="two"><label><span>${esc(tr('flow.operator'))}</span><select data-flow-config="operator">${flowOperatorOptions(c.operator || 'lt')}</select></label><label><span>${esc(temperature ? tr('automations.thresholdC') : tr('flow.value'))}</span><input type="number" step="0.1" data-flow-config="value" value="${Number(c.value ?? 0)}"></label></div>`; }
|
||||
const FLOW_DEVICE_FEATURES = Object.freeze(['power', 'mode', 'target_temperature', 'fan_speed', 'swing_vertical', 'swing_horizontal', 'quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep']);
|
||||
const FLOW_DEVICE_BOOL_FEATURES = Object.freeze(['power', 'quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep']);
|
||||
function flowDeviceFeatureDefaultValue(feature) {
|
||||
if (FLOW_DEVICE_BOOL_FEATURES.includes(feature)) return true;
|
||||
if (feature === 'mode') return 'auto';
|
||||
if (feature === 'target_temperature') return 22;
|
||||
if (feature === 'fan_speed') return 0;
|
||||
if (['swing_vertical', 'swing_horizontal'].includes(feature)) return 1;
|
||||
return true;
|
||||
}
|
||||
function flowDeviceFeatureOptions(selected) {
|
||||
return FLOW_DEVICE_FEATURES.map(feature => `<option value="${feature}" ${feature === selected ? 'selected' : ''}>${esc(deviceCommandFieldLabel(feature))}</option>`).join('');
|
||||
}
|
||||
function flowDeviceFeatureValueLabel(feature, value) {
|
||||
if (FLOW_DEVICE_BOOL_FEATURES.includes(feature)) return value === false ? tr('common.off') : tr('common.on');
|
||||
if (feature === 'mode') return modeLabel(value || 'auto');
|
||||
if (feature === 'target_temperature') return fmtTemp(value);
|
||||
if (feature === 'fan_speed') return fanLabel(Number(value));
|
||||
if (feature === 'swing_vertical') return louverPositionLabel('vertical', value);
|
||||
if (feature === 'swing_horizontal') return louverPositionLabel('horizontal', value);
|
||||
return value ?? '—';
|
||||
}
|
||||
function flowDeviceFeatureValueField(c) {
|
||||
const feature = c.feature || 'light';
|
||||
const value = c.value ?? flowDeviceFeatureDefaultValue(feature);
|
||||
if (FLOW_DEVICE_BOOL_FEATURES.includes(feature)) return `<label><span>${esc(tr('flow.value'))}</span><select data-flow-config="value"><option value="true" ${value === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${value === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label>`;
|
||||
if (feature === 'mode') return `<label><span>${esc(tr('flow.value'))}</span><select data-flow-config="value">${['auto', 'cool', 'dry', 'fan', 'heat'].map(mode => `<option value="${mode}" ${value === mode ? 'selected' : ''}>${esc(modeLabel(mode))}</option>`).join('')}</select></label>`;
|
||||
if (feature === 'target_temperature') return `<label><span>${esc(tr('flow.value'))}</span><input type="number" min="8" max="30" step="0.5" data-flow-config="value" value="${Number(value)}"></label>`;
|
||||
if (feature === 'fan_speed') return `<label><span>${esc(tr('flow.value'))}</span><select data-flow-config="value">${[0, 1, 2, 3, 4, 5].map(v => `<option value="${v}" ${Number(value) === v ? 'selected' : ''}>${esc(fanLabel(v))}</option>`).join('')}</select></label>`;
|
||||
if (feature === 'swing_vertical') return `<label><span>${esc(tr('flow.value'))}</span><select data-flow-config="value">${louverOptions('vertical', value)}</select></label>`;
|
||||
if (feature === 'swing_horizontal') return `<label><span>${esc(tr('flow.value'))}</span><select data-flow-config="value">${louverOptions('horizontal', value)}</select></label>`;
|
||||
return '';
|
||||
}
|
||||
|
||||
function flowOptionalBoolField(c, key) {
|
||||
return `<label><span>${esc(deviceCommandFieldLabel(key))}</span><select data-flow-config="${esc(key)}"><option value="" ${c[key] == null ? 'selected' : ''}>${esc(tr('actions.noChange'))}</option><option value="true" ${c[key] === true ? 'selected' : ''}>${esc(tr('common.on'))}</option><option value="false" ${c[key] === false ? 'selected' : ''}>${esc(tr('common.off'))}</option></select></label>`;
|
||||
}
|
||||
@@ -1169,6 +1207,12 @@ function updateFlowConfig(input) {
|
||||
const numeric = Boolean(sharedFlowInputLouverAxis(item)) || ['outdoor_temperature', 'device_temperature', 'zone_temperature', 'ha_numeric'].includes(item?.kind);
|
||||
node.config.value = numeric ? (input.value === '' ? null : Number(input.value)) : input.value;
|
||||
}
|
||||
else if (node.kind === 'device_feature_action' && key === 'feature') { node.config.feature = input.value; node.config.value = flowDeviceFeatureDefaultValue(input.value); }
|
||||
else if (node.kind === 'device_feature_action' && key === 'value') {
|
||||
if (FLOW_DEVICE_BOOL_FEATURES.includes(node.config.feature)) node.config.value = input.value === 'true';
|
||||
else if (['target_temperature', 'fan_speed', 'swing_vertical', 'swing_horizontal'].includes(node.config.feature)) node.config.value = Number(input.value);
|
||||
else node.config.value = input.value;
|
||||
}
|
||||
else if (node.kind === 'device_state' && key === 'field') {
|
||||
const wasLouver = ['swing_vertical', 'swing_horizontal'].includes(node.config.field);
|
||||
node.config.field = input.value;
|
||||
@@ -1189,7 +1233,7 @@ function updateFlowConfig(input) {
|
||||
else if (key === 'value' && node.kind === 'rolling_stat') node.config.value = Number(input.value || 0);
|
||||
else if (key === 'min_span' && node.kind === 'oscillates') node.config.min_span = Math.max(0.001, Number(input.value || 0.001));
|
||||
else if (key === 'min_direction_changes' && node.kind === 'oscillates') node.config.min_direction_changes = Math.max(1, Math.floor(Number(input.value || 1)));
|
||||
else if (['value', 'setpoint', 'target_temperature', 'cooldown_seconds', 'fan_speed'].includes(key) && ['outdoor_temperature', 'device_temperature', 'zone_temperature', 'ha_numeric', 'zone_thermostat', 'device_action', 'group_action', 'ha_service_action'].includes(node.kind)) node.config[key] = input.value === '' ? null : Number(input.value);
|
||||
else if (['value', 'setpoint', 'target_temperature', 'cooldown_seconds', 'fan_speed'].includes(key) && ['outdoor_temperature', 'device_temperature', 'zone_temperature', 'ha_numeric', 'zone_thermostat', 'device_action', 'device_feature_action', 'group_action', 'ha_service_action'].includes(node.kind)) node.config[key] = input.value === '' ? null : Number(input.value);
|
||||
else node.config[key] = input.value;
|
||||
app.flowDirty = true; renderFlowEditor();
|
||||
}
|
||||
|
||||
@@ -547,16 +547,19 @@ function updateAutomationTargetFields() {
|
||||
$('#automationGroupPreset').hidden = !groupTarget;
|
||||
$('#automationTargetTemperature').hidden = groupTarget;
|
||||
$('#automationDeviceSwingOptions').hidden = groupTarget;
|
||||
$('#automationDeviceFeatureOptions').hidden = groupTarget;
|
||||
$('#automationGroupHint').hidden = !groupTarget;
|
||||
form.action_device_id.required = !groupTarget;
|
||||
form.action_group_id.required = groupTarget;
|
||||
form.action_target_temperature.disabled = groupTarget;
|
||||
form.action_swing_vertical.disabled = groupTarget;
|
||||
form.action_swing_horizontal.disabled = groupTarget;
|
||||
['action_fan_speed', 'action_quiet', 'action_turbo', 'action_light', 'action_air', 'action_xfan', 'action_health', 'action_sleep'].forEach(name => { if (form.elements[name]) form.elements[name].disabled = groupTarget; });
|
||||
if (groupTarget) {
|
||||
form.action_target_temperature.value = '';
|
||||
form.action_swing_vertical.value = '';
|
||||
form.action_swing_horizontal.value = '';
|
||||
['action_fan_speed', 'action_quiet', 'action_turbo', 'action_light', 'action_air', 'action_xfan', 'action_health', 'action_sleep'].forEach(name => { if (form.elements[name]) form.elements[name].value = ''; });
|
||||
}
|
||||
[...form.action_mode.options].forEach(option => {
|
||||
if (!['dry', 'fan'].includes(option.value)) return;
|
||||
@@ -703,6 +706,8 @@ function populateAutomation(id) {
|
||||
form.action_target_temperature.value = item.action.target_temperature ?? '';
|
||||
form.action_swing_vertical.value = item.action.swing_vertical == null ? '' : String(item.action.swing_vertical);
|
||||
form.action_swing_horizontal.value = item.action.swing_horizontal == null ? '' : String(item.action.swing_horizontal);
|
||||
form.action_fan_speed.value = item.action.fan_speed == null ? '' : String(item.action.fan_speed);
|
||||
['quiet', 'turbo', 'light', 'air', 'xfan', 'health', 'sleep'].forEach(key => { const field = form.elements[`action_${key}`]; if (field) field.value = item.action[key] == null ? '' : String(item.action[key]); });
|
||||
form.enabled.checked = item.enabled;
|
||||
updateAutomationTargetFields();
|
||||
openDialog('automationDialog');
|
||||
|
||||
Reference in New Issue
Block a user