v0.8.9
This commit is contained in:
+68
-1
@@ -563,6 +563,49 @@ fn command_field_matches_device(command: &DeviceCommand, field: &str, device: &D
|
||||
}
|
||||
}
|
||||
|
||||
fn device_control_snapshot(device: &Device) -> Value {
|
||||
json!({
|
||||
"power": device.power,
|
||||
"mode": device.mode.clone(),
|
||||
"target_temperature": device.target_temperature,
|
||||
"fan_speed": device.fan_speed,
|
||||
"quiet": device.quiet,
|
||||
"sleep": device.sleep,
|
||||
"turbo": device.turbo,
|
||||
"swing_vertical": device.swing_vertical,
|
||||
"swing_horizontal": device.swing_horizontal,
|
||||
"online": device.online,
|
||||
"communication_failures": device.communication_failures,
|
||||
"last_seen": device.last_seen.clone(),
|
||||
"updated_at": device.updated_at.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn controller_settling_diagnostics(state: &AppState, device_id: &str) -> Value {
|
||||
let pending = state.pending_controller_commands.lock().await;
|
||||
let Some(expected) = pending.get(device_id).cloned() else {
|
||||
return json!({ "active": false, "reason": "none" });
|
||||
};
|
||||
let now = Instant::now();
|
||||
if now > expected.expires_at {
|
||||
let expired_by_ms = now.saturating_duration_since(expected.expires_at).as_millis().min(u64::MAX as u128) as u64;
|
||||
return json!({
|
||||
"active": false,
|
||||
"reason": "expired",
|
||||
"expired_by_ms": expired_by_ms,
|
||||
"commands": expected.commands,
|
||||
"baselines": expected.baselines,
|
||||
});
|
||||
}
|
||||
let remaining_ms = expected.expires_at.saturating_duration_since(now).as_millis().min(u64::MAX as u128) as u64;
|
||||
json!({
|
||||
"active": true,
|
||||
"remaining_ms": remaining_ms,
|
||||
"commands": expected.commands,
|
||||
"baselines": expected.baselines,
|
||||
})
|
||||
}
|
||||
|
||||
async fn suppress_expected_controller_changes(
|
||||
state: &AppState,
|
||||
device: &Device,
|
||||
@@ -1279,10 +1322,16 @@ fn set_device_manual_override(state: &AppState, zone: &mut Zone, fields: Vec<Str
|
||||
async fn detect_external_device_control(state: &AppState, before: &Device, after: &Device) -> Result<(), AppError> {
|
||||
if before.id != after.id { return Ok(()); }
|
||||
for mut zone in state.db.list_zones()?.into_iter().filter(|zone| zone.device_id == after.id) {
|
||||
let raw_fields = externally_changed_control_fields(before, after, &zone);
|
||||
let controller_settling = if raw_fields.is_empty() {
|
||||
json!({ "active": false, "reason": "no_changed_control_fields" })
|
||||
} else {
|
||||
controller_settling_diagnostics(state, &after.id).await
|
||||
};
|
||||
let fields = suppress_expected_controller_changes(
|
||||
state,
|
||||
after,
|
||||
externally_changed_control_fields(before, after, &zone),
|
||||
raw_fields.clone(),
|
||||
).await;
|
||||
if zone.device_manual_override && manual_override_matches_baseline(&zone, after) {
|
||||
persist_manual_override_clear(state, &mut zone, "gree_poll", true)?;
|
||||
@@ -1295,6 +1344,24 @@ async fn detect_external_device_control(state: &AppState, before: &Device, after
|
||||
persist_manual_override_clear(state, &mut zone, "gree_poll", false)?;
|
||||
continue;
|
||||
}
|
||||
state.log("info", "device.remote_control_detected", &format!("External/pilot control detected for {}", zone.name), json!({
|
||||
"zone_id": zone.id.clone(),
|
||||
"device_id": zone.device_id.clone(),
|
||||
"raw_fields": raw_fields,
|
||||
"detected_fields": fields.clone(),
|
||||
"before": device_control_snapshot(before),
|
||||
"after": device_control_snapshot(after),
|
||||
"controller_settling": controller_settling,
|
||||
"source": "gree_poll",
|
||||
"zone_state": {
|
||||
"enabled": zone.enabled,
|
||||
"control_owner": zone.control_owner.clone(),
|
||||
"control_source": zone.control_source.clone(),
|
||||
"demand": zone.demand,
|
||||
"local_thermostat_power": zone.local_thermostat_power,
|
||||
"temporary_quick_thermostat": zone.temporary_quick_thermostat.clone(),
|
||||
},
|
||||
}));
|
||||
set_device_manual_override(state, &mut zone, fields, "gree_poll", before)?;
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user