#[derive(Debug, Deserialize)] struct HaTestRequest { entity_id: Option, } async fn test_home_assistant( State(state): State, Json(input): Json, ) -> Result, AppError> { let settings = state.settings.read().await.clone(); let resolved_entity_id = home_assistant::resolve_entity_id(&settings.home_assistant, input.entity_id.as_deref()); let temperature = home_assistant::read_temperature( &state.http, &settings.home_assistant, resolved_entity_id.as_deref(), Some(settings.home_assistant.sensor_stale_after_seconds), ) .await .map_err(|e| AppError::Device(e.to_string()))?; Ok(Json( json!({"ok": true, "temperature_c": temperature, "entity_id": resolved_entity_id}), )) } #[derive(Debug, Deserialize)] struct HaEntityRequest { entity_id: String, } async fn inspect_home_assistant_entity( State(state): State, Json(input): Json, ) -> Result, AppError> { let settings = state.settings.read().await.clone(); let entity_id = home_assistant::resolve_entity_id(&settings.home_assistant, Some(input.entity_id.as_str())) .filter(|value| !value.trim().is_empty()) .ok_or_else(|| AppError::BadRequest("Home Assistant entity_id is required".into()))?; let payload = home_assistant::read_entity( &state.http, &settings.home_assistant, Some(entity_id.as_str()), ) .await .map_err(|e| AppError::Device(e.to_string()))?; let raw_state = payload .get("state") .and_then(Value::as_str) .unwrap_or_default() .to_string(); let available = !matches!(raw_state.as_str(), "unknown" | "unavailable" | ""); Ok(Json(json!({ "ok": true, "entity_id": entity_id, "state": raw_state, "available": available, "attributes": payload.get("attributes").cloned().unwrap_or_else(|| json!({})), "last_changed": payload.get("last_changed").cloned().unwrap_or(Value::Null), "last_updated": payload.get("last_updated").cloned().unwrap_or(Value::Null), }))) } async fn test_notifications( State(state): State, Json(mut input): Json, ) -> Result, AppError> { let old = state.settings.read().await.notifications.clone(); if input.pushover_app_token.trim().is_empty() { input.pushover_app_token = old.pushover_app_token; } if input.pushover_user_key.trim().is_empty() { input.pushover_user_key = old.pushover_user_key; } if input.slack_webhook_url.trim().is_empty() { input.slack_webhook_url = old.slack_webhook_url; } if input.discord_webhook_url.trim().is_empty() { input.discord_webhook_url = old.discord_webhook_url; } notifications::test(&state, input) .await .map_err(AppError::Device)?; Ok(Json(json!({"ok": true}))) }