v0.9.4-fix
This commit is contained in:
@@ -385,6 +385,60 @@ async fn control_zones(state: &AppState) -> Result<()> {
|
||||
}
|
||||
|
||||
let active_schedule = active_schedule_for_zone(&zone, &schedules, Local::now());
|
||||
|
||||
// Heat/Cool configuration alone is not an instruction to run forever on the implicit
|
||||
// Comfort profile. After local/global/group OFF is handed back to automation, a zone
|
||||
// with no active schedule, quick/manual target, temporary session or active scoped
|
||||
// controller stays physically OFF. This also makes gaps between schedule windows true
|
||||
// OFF periods instead of silently falling back to Comfort and recreating demand.
|
||||
if !zone_has_active_thermostat_intent(&zone, active_schedule, Utc::now()) {
|
||||
clear_compressor_pending(&mut zone, true);
|
||||
zone.effective_mode = "off".into();
|
||||
zone.effective_setpoint = None;
|
||||
zone.device_setpoint = None;
|
||||
zone.demand = false;
|
||||
zone.demand_since = None;
|
||||
zone.target_alerted_at = None;
|
||||
if zone.control_owner == "automation" {
|
||||
zone.control_reason = "Automation idle: no active schedule or explicit thermostat request".into();
|
||||
zone.control_resume_at = None;
|
||||
}
|
||||
|
||||
if device.online && device.communication_failures == 0 && device.power {
|
||||
match send_zone_command_if_owned(
|
||||
state,
|
||||
&zone.id,
|
||||
&zone.device_id,
|
||||
DeviceCommand { power: Some(false), ..Default::default() },
|
||||
).await {
|
||||
Ok(Some(updated_device)) => {
|
||||
let transition_at = Utc::now();
|
||||
if device.power != updated_device.power {
|
||||
zone.last_power_change_at = Some(transition_at);
|
||||
}
|
||||
zone.last_action_at = Some(transition_at);
|
||||
state.log("info", "zone.automation_idle_off", &format!("Zone {} remains OFF: no active thermostat intent", zone.name), json!({
|
||||
"zone_id": zone.id,
|
||||
"device_id": zone.device_id,
|
||||
"active_schedule": false,
|
||||
"manual_preset": zone.manual_preset,
|
||||
"manual_setpoint": zone.manual_setpoint,
|
||||
"control_source": zone.control_source,
|
||||
}));
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(err) => state.log("error", "zone.automation_idle_off_error", &err.to_string(), json!({
|
||||
"zone_id": zone.id, "device_id": zone.device_id
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
record_zone_history(state, &zone, outdoor_temperature, settings.poll_interval_seconds);
|
||||
let persisted_zone = persist_zone_cycle(state, &zone, cycle_started_at)?;
|
||||
state.broadcast("zone.updated", serde_json::to_value(&persisted_zone)?);
|
||||
continue;
|
||||
}
|
||||
|
||||
let (preset, target) = resolve_zone_target(&zone, active_schedule, effective_mode);
|
||||
zone.active_preset = preset;
|
||||
zone.effective_setpoint = Some(target);
|
||||
|
||||
Reference in New Issue
Block a user