This commit is contained in:
Mateusz Gruszczyński
2026-08-26 22:30:10 +02:00
parent 6a35096c5d
commit f8d6bc2304
21 changed files with 410 additions and 84 deletions
+25 -1
View File
@@ -50,6 +50,7 @@ pub fn router(state: AppState) -> Router {
.route("/api/zones", get(list_zones).post(create_zone))
.route("/api/zones/:id", get(get_zone).put(update_zone).delete(delete_zone))
.route("/api/zones/:id/control", post(update_zone_control))
.route("/api/zones/:id/manual-power", post(update_zone_manual_power))
.route("/api/zones/:id/schedule-template", post(apply_schedule_template))
.route("/api/groups", get(list_groups).post(create_group))
.route("/api/groups/:id", get(get_group).put(update_group).delete(delete_group))
@@ -197,6 +198,7 @@ async fn health(State(state): State<AppState>) -> Json<Value> {
"name": "gree-controller",
"version": env!("CARGO_PKG_VERSION"),
"uptime_seconds": state.started.elapsed().as_secs(),
"control_ready": state.initial_device_sync_complete.load(Ordering::Acquire),
"time": Utc::now(),
}))
}
@@ -220,6 +222,7 @@ async fn build_bootstrap(state: &AppState) -> Result<Value, AppError> {
"version": env!("CARGO_PKG_VERSION"),
"uptime_seconds": state.started.elapsed().as_secs(),
"auth_required": !state.config.app_token.trim().is_empty(),
"control_ready": state.initial_device_sync_complete.load(Ordering::Acquire),
}
}))
}
@@ -233,6 +236,7 @@ async fn system_info(State(state): State<AppState>) -> Result<Json<Value>, AppEr
"device_count": devices.len(),
"online_count": devices.iter().filter(|v| v.online).count(),
"simulator_count": devices.iter().filter(|v| v.simulated).count(),
"control_ready": state.initial_device_sync_complete.load(Ordering::Acquire),
"bind": state.config.bind.to_string(),
"gree_interface": if state.config.gree_interface.trim().is_empty() { "auto" } else { state.config.gree_interface.trim() },
})))
@@ -527,7 +531,7 @@ impl ZoneInput {
external_sensor_weight: self.external_sensor_weight, max_sensor_difference: self.max_sensor_difference,
device_temperature: None, external_temperature: None, current_temperature: None, control_temperature_source: "device".into(),
active_preset: "comfort".into(), manual_preset: None, manual_setpoint: None, manual_override_until: None,
device_manual_override: false, device_manual_override_since: None, device_manual_override_until: None, device_manual_override_fields: Vec::new(),
device_manual_override: false, device_manual_override_since: None, device_manual_override_until: None, device_manual_override_fields: Vec::new(), device_manual_override_baseline: None,
effective_mode: String::new(), effective_setpoint: None, device_setpoint: None,
demand: false, demand_since: None, target_alerted_at: None, last_action_at: None,
created_at, updated_at: Utc::now(),
@@ -577,6 +581,7 @@ async fn update_zone(State(state): State<AppState>, Path(id): Path<String>, Json
zone.device_manual_override_since = existing.device_manual_override_since;
zone.device_manual_override_until = existing.device_manual_override_until;
zone.device_manual_override_fields = existing.device_manual_override_fields;
zone.device_manual_override_baseline = existing.device_manual_override_baseline;
zone.effective_mode = existing.effective_mode;
zone.effective_setpoint = existing.effective_setpoint;
zone.device_setpoint = existing.device_setpoint;
@@ -665,6 +670,25 @@ async fn update_zone_control(State(state): State<AppState>, Path(id): Path<Strin
}
#[derive(Debug, Deserialize)]
struct ZoneManualPowerPatch { power: bool }
async fn update_zone_manual_power(
State(state): State<AppState>,
Path(id): Path<String>,
Json(input): Json<ZoneManualPowerPatch>,
) -> Result<Json<Value>, AppError> {
let zone = state.db.get_zone(&id)?.ok_or_else(|| AppError::NotFound(format!("zone {id}")))?;
let device = engine::send_manual_command(
&state,
&zone.device_id,
DeviceCommand { power: Some(input.power), ..Default::default() },
"zone.quick_manual_power",
).await?;
let zone = state.db.get_zone(&id)?.ok_or_else(|| AppError::NotFound(format!("zone {id}")))?;
Ok(Json(json!({"zone": zone, "device": device})))
}
async fn ensure_device_stopped_for_detach(state: &AppState, device_id: &str, source: &str) -> Result<(), AppError> {
let Some(device) = state.db.get_device(device_id)? else { return Ok(()); };
if !device.enabled {