v0.6.6
This commit is contained in:
+71
-24
@@ -594,16 +594,55 @@ async fn update_zone_control(State(state): State<AppState>, Path(id): Path<Strin
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct HouseControlPatch { mode: String }
|
||||
|
||||
async fn command_all_enabled_devices_power(state: &AppState, power: bool, source: &str) -> Result<Vec<Value>, AppError> {
|
||||
let mut failed = Vec::new();
|
||||
for device in state.db.list_devices()? {
|
||||
if !device.enabled || device.power == power { continue; }
|
||||
let command = DeviceCommand { power: Some(power), ..Default::default() };
|
||||
if let Err(err) = engine::send_command(state, &device.id, command).await {
|
||||
state.log("error", "house.power_all_error", &err.to_string(), json!({
|
||||
"device_id": device.id,
|
||||
"device_name": device.name,
|
||||
"power": power,
|
||||
"source": source,
|
||||
}));
|
||||
failed.push(json!({
|
||||
"device_id": device.id,
|
||||
"device_name": device.name,
|
||||
"error": err.to_string(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
Ok(failed)
|
||||
}
|
||||
|
||||
async fn update_house_control(State(state): State<AppState>, Json(input): Json<HouseControlPatch>) -> Result<Json<Value>, AppError> {
|
||||
if !matches!(input.mode.as_str(), "cool" | "heat" | "off") {
|
||||
return Err(AppError::BadRequest("house mode must be cool, heat or off".into()));
|
||||
}
|
||||
let mut settings = state.settings.write().await;
|
||||
settings.house_mode = input.mode;
|
||||
state.db.save_runtime_settings(&settings)?;
|
||||
let payload = public_settings(&settings);
|
||||
let mode = input.mode;
|
||||
let activate_all = mode != "off";
|
||||
let payload = {
|
||||
let mut settings = state.settings.write().await;
|
||||
settings.house_mode = mode.clone();
|
||||
// Choosing a real whole-house operating mode is an explicit request to run the
|
||||
// house climate. It therefore clears a previous global power-off. "off" keeps
|
||||
// its separate meaning: do not control, without changing master power.
|
||||
if activate_all { settings.house_power_enabled = true; }
|
||||
state.db.save_runtime_settings(&settings)?;
|
||||
public_settings(&settings)
|
||||
};
|
||||
state.broadcast("settings.updated", payload.clone());
|
||||
state.log("info", "house.mode", &format!("House mode set to {}", settings.house_mode), json!({"mode": settings.house_mode}));
|
||||
if activate_all {
|
||||
let failed = command_all_enabled_devices_power(&state, true, "house_mode").await?;
|
||||
if !failed.is_empty() {
|
||||
state.log("warn", "house.mode_power_partial", "House mode enabled master power, but some devices could not be powered on", json!({
|
||||
"mode": mode,
|
||||
"failed": failed.len(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
state.log("info", "house.mode", &format!("House mode set to {}", mode), json!({"mode": mode, "master_power_enabled": activate_all}));
|
||||
Ok(Json(payload))
|
||||
}
|
||||
|
||||
@@ -623,23 +662,7 @@ async fn update_house_power(State(state): State<AppState>, Json(input): Json<Hou
|
||||
}
|
||||
}
|
||||
|
||||
let mut failed = Vec::new();
|
||||
for device in state.db.list_devices()? {
|
||||
if !device.enabled || device.power == input.power { continue; }
|
||||
let command = DeviceCommand { power: Some(input.power), ..Default::default() };
|
||||
if let Err(err) = engine::send_command(&state, &device.id, command).await {
|
||||
state.log("error", "house.power_all_error", &err.to_string(), json!({
|
||||
"device_id": device.id,
|
||||
"device_name": device.name,
|
||||
"power": input.power,
|
||||
}));
|
||||
failed.push(json!({
|
||||
"device_id": device.id,
|
||||
"device_name": device.name,
|
||||
"error": err.to_string(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
let failed = command_all_enabled_devices_power(&state, input.power, "house_power").await?;
|
||||
|
||||
let devices = state.db.list_devices()?;
|
||||
let settings = state.settings.read().await;
|
||||
@@ -664,6 +687,17 @@ async fn update_house_preset(State(state): State<AppState>, Json(input): Json<Ho
|
||||
if !matches!(input.preset.as_str(), "auto" | "comfort" | "sleep" | "away") {
|
||||
return Err(AppError::BadRequest("house preset must be auto, comfort, sleep or away".into()));
|
||||
}
|
||||
|
||||
// A whole-house profile is also an explicit whole-house activation. This mirrors
|
||||
// selecting cooling/heating and makes the separate master-power control intuitive.
|
||||
let settings_payload = {
|
||||
let mut settings = state.settings.write().await;
|
||||
settings.house_power_enabled = true;
|
||||
state.db.save_runtime_settings(&settings)?;
|
||||
public_settings(&settings)
|
||||
};
|
||||
state.broadcast("settings.updated", settings_payload.clone());
|
||||
|
||||
let schedules = state.db.list_schedules()?;
|
||||
let mut zones = state.db.list_zones()?;
|
||||
for zone in &mut zones {
|
||||
@@ -680,8 +714,21 @@ async fn update_house_preset(State(state): State<AppState>, Json(input): Json<Ho
|
||||
state.db.save_zone(zone)?;
|
||||
state.broadcast("zone.updated", serde_json::to_value(&*zone)?);
|
||||
}
|
||||
state.log("info", "house.preset", &format!("House preset set to {}", input.preset), json!({"preset": input.preset}));
|
||||
Ok(Json(json!({"preset": input.preset, "zones": zones})))
|
||||
|
||||
let failed = command_all_enabled_devices_power(&state, true, "house_preset").await?;
|
||||
let devices = state.db.list_devices()?;
|
||||
state.log("info", "house.preset", &format!("House preset set to {}", input.preset), json!({
|
||||
"preset": input.preset,
|
||||
"master_power_enabled": true,
|
||||
"failed": failed.len(),
|
||||
}));
|
||||
Ok(Json(json!({
|
||||
"preset": input.preset,
|
||||
"zones": zones,
|
||||
"devices": devices,
|
||||
"settings": settings_payload,
|
||||
"failed": failed,
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user