v0.8.6
This commit is contained in:
+51
-22
@@ -87,13 +87,24 @@ function temporarySessionStatus(zone) {
|
||||
const safetyRemaining = secondsUntil(session.safety_expires_at);
|
||||
const hardRemaining = secondsUntil(session.expires_at);
|
||||
const startRemaining = secondsUntil(session.started_at);
|
||||
const pending = !session.activated_at && zone.local_thermostat_power !== true;
|
||||
const legacyActive = !session.activated_at
|
||||
&& Number(session.generation ?? 0) === 0
|
||||
&& (session.start_kind || 'now') === 'now'
|
||||
&& zone.local_thermostat_power === true
|
||||
&& (!session.started_at || new Date(session.started_at).getTime() <= Date.now());
|
||||
const pending = !session.activated_at && !legacyActive;
|
||||
const target = Number(session.temperature_target ?? zone.effective_setpoint ?? zone.setpoint);
|
||||
const targetText = Number.isFinite(target) ? target.toFixed(1) : '--';
|
||||
const tolerance = Number(session.tolerance_c ?? 0.3).toFixed(1);
|
||||
const operatorKey = ({within:'zones.temporaryWithinShort',at_or_below:'zones.temporaryAtOrBelowShort',at_or_above:'zones.temporaryAtOrAboveShort'})[session.temperature_operator || 'within'] || 'zones.temporaryWithinShort';
|
||||
const condition = tr(operatorKey, {target:targetText, tolerance});
|
||||
|
||||
if (session.state === 'waiting_master') {
|
||||
return {countdown:startRemaining != null ? formatExtendedCountdown(startRemaining) : '00:00', detail:tr('zones.temporaryWaitingMaster'), kind:session.finish_kind, pending:true};
|
||||
}
|
||||
if (session.state === 'paused_manual') {
|
||||
return {countdown:'—', detail:tr('zones.temporaryPausedManual'), kind:session.finish_kind, pending};
|
||||
}
|
||||
if (pending) {
|
||||
return {
|
||||
countdown:startRemaining != null && startRemaining > 0 ? formatExtendedCountdown(startRemaining) : '00:00',
|
||||
@@ -1437,6 +1448,12 @@ function populateTemporaryThermostat(id) {
|
||||
form.start_at.value = dateTimeLocalValue(new Date(Date.now() + 60 * 60 * 1000));
|
||||
form.until.value = dateTimeLocalValue(new Date(Date.now() + 2 * 60 * 60 * 1000));
|
||||
const session = zone.temporary_quick_thermostat;
|
||||
const activeSession = !!session?.activated_at || (!!session
|
||||
&& Number(session.generation ?? 0) === 0
|
||||
&& (session.start_kind || 'now') === 'now'
|
||||
&& zone.local_thermostat_power === true
|
||||
&& (!session.started_at || new Date(session.started_at).getTime() <= Date.now()));
|
||||
form.dataset.activeSession = activeSession ? 'true' : 'false';
|
||||
if (session) {
|
||||
form.start_kind.value = session.start_kind || (new Date(session.started_at).getTime() > Date.now() ? 'at' : 'now');
|
||||
if (form.start_kind.value === 'delay' && session.started_at) {
|
||||
@@ -1448,24 +1465,32 @@ function populateTemporaryThermostat(id) {
|
||||
if (session.temperature_operator) form.temperature_operator.value = session.temperature_operator;
|
||||
form.tolerance_c.value = Number(session.tolerance_c ?? 0.3).toFixed(1);
|
||||
if (session.hold_seconds) form.hold_minutes.value = Math.max(1, Math.round(Number(session.hold_seconds) / 60));
|
||||
if (session.expires_at) {
|
||||
form.until.value = dateTimeLocalValue(session.expires_at);
|
||||
if (session.finish_kind === 'duration') {
|
||||
const reference = (!session.activated_at && zone.local_thermostat_power !== true && session.started_at)
|
||||
? new Date(session.started_at).getTime()
|
||||
: Date.now();
|
||||
form.duration_minutes.value = Math.max(1, Math.ceil((new Date(session.expires_at).getTime() - reference) / 60000));
|
||||
if (session.finish_kind === 'duration') {
|
||||
if (session.duration_seconds) {
|
||||
form.duration_minutes.value = Math.max(1, Math.round(Number(session.duration_seconds) / 60));
|
||||
} else if (session.expires_at) {
|
||||
const base = new Date(session.activated_at || session.started_at || Date.now()).getTime();
|
||||
form.duration_minutes.value = Math.max(1, Math.round((new Date(session.expires_at).getTime() - base) / 60000));
|
||||
}
|
||||
}
|
||||
if (session.safety_expires_at) {
|
||||
const reference = (!session.activated_at && zone.local_thermostat_power !== true && session.started_at)
|
||||
? new Date(session.started_at).getTime()
|
||||
: Date.now();
|
||||
form.max_duration_minutes.value = Math.max(1, Math.ceil((new Date(session.safety_expires_at).getTime() - reference) / 60000));
|
||||
if (session.expires_at && session.finish_kind !== 'duration') form.until.value = dateTimeLocalValue(session.expires_at);
|
||||
if (session.safety_duration_seconds) {
|
||||
form.max_duration_minutes.value = Math.max(1, Math.round(Number(session.safety_duration_seconds) / 60));
|
||||
} else if (session.safety_expires_at) {
|
||||
const base = new Date(session.activated_at || session.started_at || Date.now()).getTime();
|
||||
form.max_duration_minutes.value = Math.max(1, Math.round((new Date(session.safety_expires_at).getTime() - base) / 60000));
|
||||
} else if (['temperature_reached','temperature_stable'].includes(session.finish_kind)) {
|
||||
form.max_duration_minutes.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Once ownership has started, the historical start is informational. Editing changes only
|
||||
// target/finish rules; rescheduling requires stopping the session and creating a new one.
|
||||
form.start_kind.disabled = activeSession;
|
||||
form.start_delay_minutes.disabled = activeSession;
|
||||
form.start_at.disabled = activeSession;
|
||||
$$('[data-temporary-start-delay]').forEach(button => { button.disabled = activeSession; });
|
||||
|
||||
updateTemporaryThermostatFields();
|
||||
const active = $('#temporaryThermostatActive');
|
||||
active.hidden = !session;
|
||||
@@ -2172,9 +2197,13 @@ $('#temporaryThermostatStop')?.addEventListener('click', async event => {
|
||||
$('#temporaryThermostatForm')?.addEventListener('submit', async event => {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget, raw = Object.fromEntries(new FormData(form));
|
||||
const request = {start_kind:raw.start_kind, finish_kind:raw.finish_kind, target_temperature:parseDecimal(raw.target_temperature)};
|
||||
if (raw.start_kind === 'delay') request.start_delay_minutes = Number(raw.start_delay_minutes);
|
||||
if (raw.start_kind === 'at') {
|
||||
const activeSession = form.dataset.activeSession === 'true';
|
||||
const zone = app.zones.find(item => item.id === raw.zone_id);
|
||||
const existingSession = zone?.temporary_quick_thermostat;
|
||||
const startKind = activeSession ? (existingSession?.start_kind || 'now') : raw.start_kind;
|
||||
const request = {start_kind:startKind, finish_kind:raw.finish_kind, target_temperature:parseDecimal(raw.target_temperature)};
|
||||
if (!activeSession && startKind === 'delay') request.start_delay_minutes = Number(raw.start_delay_minutes);
|
||||
if (!activeSession && startKind === 'at') {
|
||||
const startAt = new Date(raw.start_at);
|
||||
if (!Number.isFinite(startAt.getTime())) return toast(tr('zones.temporaryInvalidStartAt'), true);
|
||||
request.start_at = startAt.toISOString();
|
||||
@@ -2183,9 +2212,9 @@ $('#temporaryThermostatForm')?.addEventListener('submit', async event => {
|
||||
if (raw.finish_kind === 'until') {
|
||||
const until = new Date(raw.until);
|
||||
if (!Number.isFinite(until.getTime())) return toast(tr('zones.temporaryInvalidUntil'), true);
|
||||
const effectiveStart = raw.start_kind === 'delay'
|
||||
? new Date(Date.now() + Number(raw.start_delay_minutes) * 60000)
|
||||
: raw.start_kind === 'at' ? new Date(raw.start_at) : new Date();
|
||||
const effectiveStart = activeSession ? new Date()
|
||||
: startKind === 'delay' ? new Date(Date.now() + Number(raw.start_delay_minutes) * 60000)
|
||||
: startKind === 'at' ? new Date(raw.start_at) : new Date();
|
||||
if (Number.isFinite(effectiveStart.getTime()) && until <= effectiveStart) return toast(tr('zones.temporaryEndAfterStart'), true);
|
||||
request.until = until.toISOString();
|
||||
}
|
||||
@@ -2198,9 +2227,9 @@ $('#temporaryThermostatForm')?.addEventListener('submit', async event => {
|
||||
}
|
||||
try {
|
||||
$('#temporaryThermostatSubmit').disabled = true;
|
||||
const zone = await api(`/api/zones/${encodeURIComponent(raw.zone_id)}/control`, {method:'POST', body:{temporary_quick_thermostat:request}});
|
||||
const index = app.zones.findIndex(item => item.id === zone.id); if (index >= 0) app.zones[index] = zone;
|
||||
form.closest('dialog').close(); renderAll(); scheduleControlPlanLoad(); toast(tr(raw.start_kind === 'now' ? 'zones.temporaryStarted' : 'zones.temporaryScheduledToast'));
|
||||
const updatedZone = await api(`/api/zones/${encodeURIComponent(raw.zone_id)}/control`, {method:'POST', body:{temporary_quick_thermostat:request}});
|
||||
const index = app.zones.findIndex(item => item.id === updatedZone.id); if (index >= 0) app.zones[index] = updatedZone;
|
||||
form.closest('dialog').close(); renderAll(); scheduleControlPlanLoad(); toast(tr(activeSession || startKind === 'now' ? 'zones.temporaryStarted' : 'zones.temporaryScheduledToast'));
|
||||
} catch (error) { toast(error.message, true); }
|
||||
finally { $('#temporaryThermostatSubmit').disabled = false; }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user