This commit is contained in:
Mateusz Gruszczyński
2026-09-01 11:10:28 +02:00
parent 3479ed750d
commit 9f08d7ccf9
30 changed files with 559 additions and 150 deletions
+9 -9
View File
@@ -170,15 +170,15 @@ async fn send_command_locked_inner(
}
fn record_device_transition_timestamps(state: &AppState, before: &Device, after: &Device) -> Result<(), AppError> {
if before.power == after.power && before.mode == after.mode { return Ok(()); }
let now = Utc::now();
for mut zone in state.db.list_zones()?.into_iter().filter(|zone| zone.device_id == after.id) {
if before.power != after.power { zone.last_power_change_at = Some(now); }
if before.mode != after.mode { zone.last_mode_change_at = Some(now); }
// Do not bump zone.updated_at here: an in-flight thermostat cycle uses that field
// as its optimistic snapshot guard. The cycle mirrors these timestamps into its own
// computed Zone after a successful automatic command.
state.db.save_zone(&zone)?;
let power_changed = before.power != after.power;
let mode_changed = before.mode != after.mode;
if !power_changed && !mode_changed { return Ok(()); }
// This function is often called while the device lock is held, so acquiring a zone lock
// here would invert the global zone -> device order. Merge only these timestamp fields
// with a DB compare-and-swap instead of saving a stale whole-zone snapshot.
for zone in state.db.merge_zone_device_transition_timestamps(
&after.id, power_changed, mode_changed, Utc::now(),
)? {
state.broadcast("zone.updated", serde_json::to_value(&zone)?);
}
Ok(())