v0.9.2
This commit is contained in:
@@ -23,16 +23,19 @@ pub fn set_local_thermostat_power(zone: &mut Zone, power: bool, now: DateTime<Ut
|
||||
changed
|
||||
}
|
||||
|
||||
/// Re-arm a local-OFF hand-back after a temporary direct/manual device takeover.
|
||||
/// The countdown must start from the moment that manual control ends, not from the
|
||||
/// older OFF action that happened before the takeover.
|
||||
/// Re-arm a timed local-OFF hand-back after a temporary direct/manual device takeover.
|
||||
/// An OFF with no resume_at is intentionally indefinite (for example a group OFF that
|
||||
/// physically powers members down while releasing group ownership) and must never gain
|
||||
/// a 15-minute timer merely because another ownership mode ended.
|
||||
fn rearm_local_thermostat_resume(zone: &mut Zone, now: DateTime<Utc>) -> bool {
|
||||
if zone.local_thermostat_power != Some(false) { return false; }
|
||||
if zone.local_thermostat_power != Some(false) || zone.local_thermostat_resume_at.is_none() { return false; }
|
||||
set_local_thermostat_power(zone, false, now)
|
||||
}
|
||||
|
||||
fn local_thermostat_handback_is_active(zone: &Zone) -> bool {
|
||||
zone.local_thermostat_power == Some(false) && !zone.device_manual_override
|
||||
zone.local_thermostat_power == Some(false)
|
||||
&& zone.local_thermostat_resume_at.is_some()
|
||||
&& !zone.device_manual_override
|
||||
}
|
||||
|
||||
/// Clear only the ordinary local Quick Thermostat. Temporary Quick Thermostat state is
|
||||
|
||||
@@ -362,19 +362,11 @@ async fn expire_local_thermostat_overrides(state: &AppState, zones: &mut [Zone],
|
||||
// Do not let the old timer expire underneath someone who is actively controlling
|
||||
// the unit. When that takeover ends and the device returns to OFF, the deadline is
|
||||
// re-armed from that moment.
|
||||
// Only an explicit timed local-OFF participates in automatic hand-back.
|
||||
// local_thermostat_power=false with resume_at=None is a deliberate indefinite OFF
|
||||
// (notably the state produced by group OFF) and must stay off until the user/group
|
||||
// explicitly turns it back on or resumes automation.
|
||||
if !local_thermostat_handback_is_active(zone) { continue; }
|
||||
if zone.local_thermostat_resume_at.is_none() {
|
||||
// Upgrade safety for a persisted 0.7.10/0.7.11 local-OFF state: old releases
|
||||
// had no hand-back deadline, so start one from the first cycle after upgrade.
|
||||
set_local_thermostat_power(zone, false, now.clone());
|
||||
zone.updated_at = now.clone();
|
||||
state.db.save_zone(zone)?;
|
||||
state.broadcast("zone.updated", serde_json::to_value(&*zone)?);
|
||||
state.log("info", "zone.local_thermostat_resume_scheduled", &format!("Local thermostat hand-back scheduled for {}", zone.name), json!({
|
||||
"zone_id": zone.id, "device_id": zone.device_id, "delay_minutes": LOCAL_THERMOSTAT_RESUME_DELAY_MINUTES
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
let expired = zone.local_thermostat_resume_at.as_ref().map(|at| at <= &now).unwrap_or(false);
|
||||
if !expired { continue; }
|
||||
reset_local_thermostat_override(zone);
|
||||
|
||||
@@ -439,6 +439,27 @@ mod tests {
|
||||
assert_eq!(effective_zone_mode(&zone, "off"), "cool");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indefinite_local_off_does_not_create_or_rearm_handback() {
|
||||
let mut zone = test_zone("device");
|
||||
zone.local_thermostat_power = Some(false);
|
||||
zone.local_thermostat_resume_at = None;
|
||||
assert!(!local_thermostat_handback_is_active(&zone));
|
||||
assert!(!rearm_local_thermostat_resume(&mut zone, Utc::now()));
|
||||
assert_eq!(zone.local_thermostat_power, Some(false));
|
||||
assert!(zone.local_thermostat_resume_at.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timed_local_off_remains_blocking_until_explicit_deadline() {
|
||||
let mut zone = test_zone("device");
|
||||
let now = Utc::now();
|
||||
set_local_thermostat_power(&mut zone, false, now);
|
||||
assert!(local_thermostat_handback_is_active(&zone));
|
||||
assert_eq!(zone.local_thermostat_power, Some(false));
|
||||
assert!(zone.local_thermostat_resume_at.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_thermostat_off_restarts_backend_handback_deadline() {
|
||||
let mut zone = test_zone("device");
|
||||
|
||||
Reference in New Issue
Block a user