v0.9.4-fix
This commit is contained in:
+34
-17
@@ -20,25 +20,36 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
||||
let configured_effective_mode_owned = effective_zone_mode(&zone, &settings.house_mode);
|
||||
refresh_control_ownership(&mut zone, true);
|
||||
let configured_effective_mode = configured_effective_mode_owned.as_str();
|
||||
let target_mode = if configured_effective_mode == "off" { zone.mode.as_str() } else { configured_effective_mode };
|
||||
let active_for_target = active_schedule_for_zone(&zone, &schedules, now);
|
||||
let has_intent_source = zone_has_thermostat_intent_source(&zone, active_for_target, Utc::now());
|
||||
let automation_idle = zone.enabled
|
||||
&& !zone.device_manual_override
|
||||
&& zone.local_thermostat_power != Some(false)
|
||||
&& configured_effective_mode != "off"
|
||||
&& !has_intent_source;
|
||||
let automatic_active = zone.enabled
|
||||
&& !zone.device_manual_override
|
||||
&& zone.local_thermostat_power != Some(false)
|
||||
&& configured_effective_mode != "off"
|
||||
&& has_intent_source;
|
||||
let manual_device_mode = device.map(|item| if item.power { item.mode.as_str() } else { "off" });
|
||||
let effective_mode = if zone.device_manual_override {
|
||||
manual_device_mode.unwrap_or(configured_effective_mode)
|
||||
} else if zone.local_thermostat_power == Some(false) {
|
||||
"off"
|
||||
} else {
|
||||
} else if automatic_active {
|
||||
configured_effective_mode
|
||||
} else {
|
||||
"off"
|
||||
};
|
||||
|
||||
// Keep the thermostat target readable even while the zone/group/house control is off.
|
||||
// Home Assistant climate entities otherwise expose target_temperature as unknown.
|
||||
let target_mode = if configured_effective_mode == "off" { zone.mode.as_str() } else { configured_effective_mode };
|
||||
let active_for_target = active_schedule_for_zone(&zone, &schedules, now);
|
||||
let (resolved_preset, resolved_target) = resolve_zone_target(&zone, active_for_target, target_mode);
|
||||
let active = if effective_mode == "off" { None } else { active_for_target };
|
||||
let next_events = if effective_mode == "off" {
|
||||
let active = if automatic_active { active_for_target } else { None };
|
||||
// Future schedule events remain visible while the zone is currently idle between
|
||||
// windows; idle must not be confused with a disabled schedule system.
|
||||
let next_events = if configured_effective_mode == "off" {
|
||||
Vec::new()
|
||||
} else {
|
||||
next_schedule_events(&zone, &schedules, effective_mode, now, 8)
|
||||
next_schedule_events(&zone, &schedules, configured_effective_mode, now, 8)
|
||||
};
|
||||
for event in next_events.iter().take(2) {
|
||||
let mut event = event.clone();
|
||||
@@ -57,23 +68,25 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
||||
mode: effective_mode.to_string(),
|
||||
configured_mode: zone.mode.clone(),
|
||||
inherit_house_mode: zone.inherit_house_mode,
|
||||
preset: resolved_preset,
|
||||
preset: if has_intent_source { resolved_preset } else { "auto".into() },
|
||||
preset_override: zone.manual_preset.clone(),
|
||||
current_temperature: zone.current_temperature,
|
||||
target_temperature: if zone.device_manual_override || !zone.enabled || effective_mode == "off" {
|
||||
target_temperature: if !has_intent_source {
|
||||
None
|
||||
} else if zone.device_manual_override || !zone.enabled || effective_mode == "off" {
|
||||
// A remote/manual takeover may leave a physical standby target persisted in the
|
||||
// zone runtime snapshot. Never publish that value as the thermostat target.
|
||||
// zone runtime snapshot. Publish only the underlying explicit thermostat target.
|
||||
Some(resolved_target)
|
||||
} else {
|
||||
zone.effective_setpoint.or(Some(resolved_target))
|
||||
},
|
||||
device_setpoint: device.filter(|item| item.power).map(|item| item.target_temperature),
|
||||
desired_power: zone.enabled && effective_mode != "off" && !zone.device_manual_override,
|
||||
desired_power: automatic_active,
|
||||
desired_mode: effective_mode.to_string(),
|
||||
actual_power: device.map(|item| item.power),
|
||||
actual_mode: device.map(|item| if item.power { item.mode.clone() } else { "off".into() }),
|
||||
actual_setpoint: device.filter(|item| item.power).map(|item| item.target_temperature),
|
||||
demand: zone.enabled && effective_mode != "off" && !zone.device_manual_override && zone.demand,
|
||||
demand: automatic_active && zone.demand,
|
||||
control_source: zone.control_temperature_source.clone(),
|
||||
manual_override_until: zone.manual_override_until,
|
||||
local_thermostat_power: zone.local_thermostat_power,
|
||||
@@ -84,8 +97,12 @@ pub async fn build_control_plan(state: &AppState) -> Result<ControlPlan, AppErro
|
||||
control_command_source: zone.control_source.clone(),
|
||||
control_since: zone.control_since,
|
||||
resume_at: zone.control_resume_at,
|
||||
control_reason: zone.control_reason.clone(),
|
||||
blocked_reason: if zone.device_manual_override { Some("manual_override".into()) } else if zone.lockout_until.map(|until| until > Utc::now()).unwrap_or(false) { Some(zone.lockout_reason.clone().unwrap_or_else(|| "lockout".into())) } else if !zone.enabled { Some("zone_disabled".into()) } else if device.map(|d| !d.online || d.communication_failures > 0).unwrap_or(true) { Some("offline".into()) } else { None },
|
||||
control_reason: if automation_idle {
|
||||
"Automation idle: no active schedule or explicit thermostat request".into()
|
||||
} else {
|
||||
zone.control_reason.clone()
|
||||
},
|
||||
blocked_reason: if zone.device_manual_override { Some("manual_override".into()) } else if zone.lockout_until.map(|until| until > Utc::now()).unwrap_or(false) { Some(zone.lockout_reason.clone().unwrap_or_else(|| "lockout".into())) } else if !zone.enabled { Some("zone_disabled".into()) } else if device.map(|d| !d.online || d.communication_failures > 0).unwrap_or(true) { Some("offline".into()) } else if automation_idle { Some("no_active_thermostat_intent".into()) } else { None },
|
||||
lockout_until: zone.lockout_until,
|
||||
current_schedule_id: active.map(|item| item.id.clone()),
|
||||
current_schedule_name: active.map(|item| item.name.clone()),
|
||||
|
||||
Reference in New Issue
Block a user