This commit is contained in:
Mateusz Gruszczyński
2026-09-01 15:38:46 +02:00
parent 1392114c1e
commit 3000dbaf01
26 changed files with 7520 additions and 2299 deletions
+25 -25
View File
@@ -75,7 +75,7 @@ function validationMessageFor(field) {
if (field.validity?.valueMissing) return tr('validation.required');
if (field.validity?.typeMismatch) return field.type === 'url' ? tr('validation.url') : tr('validation.invalid');
if (field.validity?.rangeUnderflow || field.validity?.rangeOverflow) {
return tr('validation.range', {min:field.min || '—', max:field.max || '—'});
return tr('validation.range', { min: field.min || '—', max: field.max || '—' });
}
if (field.validity?.badInput || field.validity?.stepMismatch || field.validity?.patternMismatch) return tr('validation.invalid');
return field.validity?.valid === false ? tr('validation.invalid') : '';
@@ -95,7 +95,7 @@ function validateDecimalField(form, name, min, max) {
if (!field || field.disabled) return true;
const value = parseDecimal(field.value);
if (!Number.isFinite(value) || value < min || value > max) {
showFieldError(field, tr('validation.range', {min, max}));
showFieldError(field, tr('validation.range', { min, max }));
return false;
}
return true;
@@ -114,7 +114,7 @@ function validateForm(form) {
if (form.id === 'homeAssistantForm') {
const url = form.elements.ha_url;
if (url?.value && !httpUrlValid(url.value)) { showFieldError(url, tr('validation.url')); valid = false; }
['ha_entity_id','ha_outdoor_entity_id'].forEach(name => {
['ha_entity_id', 'ha_outdoor_entity_id'].forEach(name => {
const field = form.elements[name];
if (field?.value && !entityIdValid(field.value)) { showFieldError(field, tr('validation.entityId')); valid = false; }
});
@@ -134,14 +134,14 @@ function validateForm(form) {
valid = false;
}
const zoneDecimalFields = [
['cool_comfort_setpoint',8,30],['cool_sleep_setpoint',8,30],['cool_away_setpoint',8,30],
['heat_comfort_setpoint',8,30],['heat_sleep_setpoint',8,30],['heat_away_setpoint',8,30],
['max_sensor_difference',0.1,20],['standby_offset_c',0.5,8],
['cool_comfort_setpoint', 8, 30], ['cool_sleep_setpoint', 8, 30], ['cool_away_setpoint', 8, 30],
['heat_comfort_setpoint', 8, 30], ['heat_sleep_setpoint', 8, 30], ['heat_away_setpoint', 8, 30],
['max_sensor_difference', 0.1, 20], ['standby_offset_c', 0.5, 8],
...(form.elements.separate_hysteresis?.checked
? [['cool_hysteresis',0.1,5],['heat_hysteresis',0.1,5]]
: [['hysteresis',0.1,5]])
? [['cool_hysteresis', 0.1, 5], ['heat_hysteresis', 0.1, 5]]
: [['hysteresis', 0.1, 5]])
];
zoneDecimalFields.forEach(([name,min,max]) => { if (!validateDecimalField(form,name,min,max)) valid = false; });
zoneDecimalFields.forEach(([name, min, max]) => { if (!validateDecimalField(form, name, min, max)) valid = false; });
}
if (!valid) {
@@ -149,8 +149,8 @@ function validateForm(form) {
const first = $('[aria-invalid="true"]', form);
const pane = first?.closest?.('[data-settings-pane]');
if (pane?.dataset.settingsPane) setSettingsTab(pane.dataset.settingsPane);
first?.focus({preventScroll:true});
first?.scrollIntoView({behavior:'smooth', block:'center'});
first?.focus({ preventScroll: true });
first?.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
return valid;
}
@@ -160,12 +160,12 @@ function apiErrorField(form, message) {
const candidates = [];
if (text.includes('influx')) candidates.push('influx_url');
if (text.includes('home assistant') || text.includes('ha ')) candidates.push('ha_url');
if (text.includes('entity')) candidates.push('ha_entity_id','ha_outdoor_entity_id');
if (text.includes('device')) candidates.push('device_id','action_device_id','trigger_device_id');
if (text.includes('temperature') || text.includes('setpoint')) candidates.push('target_temperature','cool_comfort_setpoint','action_target_temperature');
if (text.includes('name')) candidates.push('name','controller_id');
if (text.includes('token')) candidates.push('ha_token','influx_token','pushover_app_token');
if (text.includes('url')) candidates.push('ha_url','influx_url','slack_webhook_url','discord_webhook_url');
if (text.includes('entity')) candidates.push('ha_entity_id', 'ha_outdoor_entity_id');
if (text.includes('device')) candidates.push('device_id', 'action_device_id', 'trigger_device_id');
if (text.includes('temperature') || text.includes('setpoint')) candidates.push('target_temperature', 'cool_comfort_setpoint', 'action_target_temperature');
if (text.includes('name')) candidates.push('name', 'controller_id');
if (text.includes('token')) candidates.push('ha_token', 'influx_token', 'pushover_app_token');
if (text.includes('url')) candidates.push('ha_url', 'influx_url', 'slack_webhook_url', 'discord_webhook_url');
return candidates.map(name => form?.elements?.[name]).find(Boolean) || null;
}
@@ -178,8 +178,8 @@ function presentFormError(form, error) {
if (field) {
const pane = field.closest?.('[data-settings-pane]');
if (pane?.dataset.settingsPane) setSettingsTab(pane.dataset.settingsPane);
field.focus({preventScroll:true});
field.scrollIntoView({behavior:'smooth', block:'center'});
field.focus({ preventScroll: true });
field.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
toast(message, true);
}
@@ -207,18 +207,18 @@ function setFormBusy(form, busy, busyKey = 'actions.saving') {
}
}
async function runFormTask(form, task, {busyKey='actions.saving', validate=true}={}) {
if (!form || form.dataset.submitting === 'true') return {ok:false, duplicate:true};
if (validate && !validateForm(form)) return {ok:false, validation:true};
async function runFormTask(form, task, { busyKey = 'actions.saving', validate = true } = {}) {
if (!form || form.dataset.submitting === 'true') return { ok: false, duplicate: true };
if (validate && !validateForm(form)) return { ok: false, validation: true };
clearFormErrors(form);
setFormBusy(form, true, busyKey);
try {
const value = await task();
markFormClean(form);
return {ok:true, value};
return { ok: true, value };
} catch (error) {
presentFormError(form, error);
return {ok:false, error};
return { ok: false, error };
} finally {
setFormBusy(form, false);
}
@@ -229,7 +229,7 @@ function restoreTrackedForm(form) {
if (form.id === 'settingsForm') renderSettings();
else if (form.id === 'nightModeForm') renderNightSettings();
else if (form.id === 'homeAssistantForm') {
app.sensorAliases = {...(app.settings?.home_assistant?.sensor_aliases || {})};
app.sensorAliases = { ...(app.settings?.home_assistant?.sensor_aliases || {}) };
renderHomeAssistantSettings();
} else markFormClean(form);
}