v0.13.7
This commit is contained in:
+3
-2
@@ -4,12 +4,13 @@ use crate::{
|
||||
home_assistant, influxdb,
|
||||
models::{
|
||||
ApiTokenInfo, ApplicationSettings, Automation, ClimateGroup, ConfigurationExport,
|
||||
DebugSettings, Device, DeviceCommand, DevicePatch, DiscoveryRequest, GreeSettings,
|
||||
DebugSettings, Device, DeviceCommand, DevicePatch, DiscoveryRequest, Flow, GreeSettings,
|
||||
GroupControlPatch, HaReading, HistorySettings, HomeAssistantSettings,
|
||||
HomeAssistantSettingsUpdate, HomeAssistantSettingsView, InfluxDbSettings,
|
||||
InfluxDbSettingsUpdate, InfluxDbSettingsView, ManualDeviceRequest, NightModeSettings,
|
||||
NotificationSettings, NotificationSettingsUpdate, NotificationSettingsView, Reading,
|
||||
RuntimeSettings, Schedule, TemporaryQuickThermostat, TemporaryQuickThermostatRequest, Zone,
|
||||
RuntimeSettings, Schedule, SettingsSnapshot, TemporaryQuickThermostat,
|
||||
TemporaryQuickThermostatRequest, Zone,
|
||||
ZoneControlPatch, ZoneReading,
|
||||
},
|
||||
notifications,
|
||||
|
||||
@@ -70,6 +70,19 @@ fn home_assistant_settings(settings: &RuntimeSettings) -> HomeAssistantSettingsV
|
||||
}
|
||||
}
|
||||
|
||||
fn settings_snapshot(settings: &RuntimeSettings) -> SettingsSnapshot {
|
||||
SettingsSnapshot {
|
||||
application: application_settings(settings),
|
||||
gree: gree_settings(settings),
|
||||
history: history_settings(settings),
|
||||
influxdb: influxdb_settings(settings),
|
||||
notifications: notification_settings(settings),
|
||||
night: settings.night_mode.clone(),
|
||||
home_assistant: home_assistant_settings(settings),
|
||||
debug: settings.debug.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_application_settings(State(state): State<AppState>) -> Json<ApplicationSettings> {
|
||||
Json(application_settings(&*state.settings.read().await))
|
||||
}
|
||||
|
||||
+93
-52
@@ -1,3 +1,42 @@
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
struct HouseSnapshot {
|
||||
mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
struct SystemInfoResponse {
|
||||
version: &'static str,
|
||||
uptime_seconds: u64,
|
||||
auth_required: bool,
|
||||
control_ready: bool,
|
||||
database: String,
|
||||
device_count: usize,
|
||||
online_count: usize,
|
||||
simulator_count: usize,
|
||||
bind: String,
|
||||
base_path: String,
|
||||
gree_interface: String,
|
||||
gree_received_frames: u64,
|
||||
gree_received_frames_by_device: std::collections::HashMap<String, u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
struct BootstrapResponse {
|
||||
devices: Vec<Device>,
|
||||
zones: Vec<Zone>,
|
||||
groups: Vec<ClimateGroup>,
|
||||
schedules: Vec<Schedule>,
|
||||
automations: Vec<Automation>,
|
||||
flows: Vec<Flow>,
|
||||
access_tokens: Vec<ApiTokenInfo>,
|
||||
settings: SettingsSnapshot,
|
||||
house: HouseSnapshot,
|
||||
outdoor_temperature: Option<f64>,
|
||||
control_plan: crate::models::ControlPlan,
|
||||
control_plan_revision: u64,
|
||||
system: SystemInfoResponse,
|
||||
}
|
||||
|
||||
async fn health(State(state): State<AppState>) -> Json<Value> {
|
||||
Json(json!({
|
||||
"status": "ok",
|
||||
@@ -9,64 +48,66 @@ async fn health(State(state): State<AppState>) -> Json<Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
async fn bootstrap(State(state): State<AppState>) -> Result<Json<Value>, AppError> {
|
||||
async fn bootstrap(State(state): State<AppState>) -> Result<Json<BootstrapResponse>, AppError> {
|
||||
Ok(Json(build_bootstrap(&state).await?))
|
||||
}
|
||||
|
||||
async fn build_bootstrap(state: &AppState) -> Result<Value, AppError> {
|
||||
let settings = state.settings.read().await.clone();
|
||||
async fn build_bootstrap(state: &AppState) -> Result<BootstrapResponse, AppError> {
|
||||
let (settings, house_mode) = {
|
||||
let settings = state.settings.read().await;
|
||||
(settings_snapshot(&settings), settings.house_mode.clone())
|
||||
};
|
||||
let devices = state.db.list_devices()?;
|
||||
let device_count = devices.len();
|
||||
let online_count = devices.iter().filter(|value| value.online).count();
|
||||
let simulator_count = devices.iter().filter(|value| value.simulated).count();
|
||||
let (received_frames_total, received_frames_by_device) = state.gree.received_frame_stats();
|
||||
let system = build_system_info(state, &devices);
|
||||
let control_plan = engine::get_control_plan_snapshot(state).await?;
|
||||
Ok(json!({
|
||||
"devices": devices,
|
||||
"zones": state.db.list_zones()?,
|
||||
"groups": state.db.list_groups()?,
|
||||
"schedules": state.db.list_schedules()?,
|
||||
"automations": state.db.list_automations()?,
|
||||
"flows": state.db.list_flows()?,
|
||||
"access_tokens": state.db.list_api_tokens()?,
|
||||
"house": {"mode": settings.house_mode},
|
||||
"outdoor_temperature": *state.outdoor_temperature.read().await,
|
||||
"control_plan": control_plan.plan.as_ref(),
|
||||
"control_plan_revision": control_plan.revision,
|
||||
"system": {
|
||||
"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),
|
||||
"database": state.config.database.display().to_string(),
|
||||
"device_count": device_count,
|
||||
"online_count": online_count,
|
||||
"simulator_count": simulator_count,
|
||||
"bind": state.config.bind.to_string(),
|
||||
"base_path": if state.config.base_path.is_empty() { "/" } else { state.config.base_path.as_str() },
|
||||
"gree_interface": if state.config.gree_interface.trim().is_empty() { "auto" } else { state.config.gree_interface.trim() },
|
||||
"gree_received_frames": received_frames_total,
|
||||
"gree_received_frames_by_device": received_frames_by_device,
|
||||
}
|
||||
}))
|
||||
|
||||
Ok(BootstrapResponse {
|
||||
devices,
|
||||
zones: state.db.list_zones()?,
|
||||
groups: state.db.list_groups()?,
|
||||
schedules: state.db.list_schedules()?,
|
||||
automations: state.db.list_automations()?,
|
||||
flows: state.db.list_flows()?,
|
||||
access_tokens: state.db.list_api_tokens()?,
|
||||
settings,
|
||||
house: HouseSnapshot { mode: house_mode },
|
||||
outdoor_temperature: *state.outdoor_temperature.read().await,
|
||||
control_plan: control_plan.plan.as_ref().clone(),
|
||||
control_plan_revision: control_plan.revision,
|
||||
system,
|
||||
})
|
||||
}
|
||||
|
||||
async fn system_info(State(state): State<AppState>) -> Result<Json<Value>, AppError> {
|
||||
let devices = state.db.list_devices()?;
|
||||
fn build_system_info(state: &AppState, devices: &[Device]) -> SystemInfoResponse {
|
||||
let (received_frames_total, received_frames_by_device) = state.gree.received_frame_stats();
|
||||
Ok(Json(json!({
|
||||
"version": env!("CARGO_PKG_VERSION"),
|
||||
"uptime_seconds": state.started.elapsed().as_secs(),
|
||||
"database": state.config.database.display().to_string(),
|
||||
"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),
|
||||
"auth_required": !state.config.app_token.trim().is_empty(),
|
||||
"bind": state.config.bind.to_string(),
|
||||
"base_path": if state.config.base_path.is_empty() { "/" } else { state.config.base_path.as_str() },
|
||||
"gree_interface": if state.config.gree_interface.trim().is_empty() { "auto" } else { state.config.gree_interface.trim() },
|
||||
"gree_received_frames": received_frames_total,
|
||||
"gree_received_frames_by_device": received_frames_by_device,
|
||||
})))
|
||||
SystemInfoResponse {
|
||||
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),
|
||||
database: state.config.database.display().to_string(),
|
||||
device_count: devices.len(),
|
||||
online_count: devices.iter().filter(|device| device.online).count(),
|
||||
simulator_count: devices.iter().filter(|device| device.simulated).count(),
|
||||
bind: state.config.bind.to_string(),
|
||||
base_path: if state.config.base_path.is_empty() {
|
||||
"/".to_string()
|
||||
} else {
|
||||
state.config.base_path.clone()
|
||||
},
|
||||
gree_interface: if state.config.gree_interface.trim().is_empty() {
|
||||
"auto".to_string()
|
||||
} else {
|
||||
state.config.gree_interface.trim().to_string()
|
||||
},
|
||||
gree_received_frames: received_frames_total,
|
||||
gree_received_frames_by_device: received_frames_by_device,
|
||||
}
|
||||
}
|
||||
|
||||
async fn system_info(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<SystemInfoResponse>, AppError> {
|
||||
let devices = state.db.list_devices()?;
|
||||
Ok(Json(build_system_info(&state, &devices)))
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fn control_plan_ws_message(snapshot: &crate::state::ControlPlanSnapshot) -> Valu
|
||||
async fn send_ws_bootstrap(state: &AppState, socket: &mut WebSocket) -> Result<Option<u64>, ()> {
|
||||
let (message, revision) = match build_bootstrap(state).await {
|
||||
Ok(data) => {
|
||||
let revision = data.get("control_plan_revision").and_then(Value::as_u64);
|
||||
let revision = Some(data.control_plan_revision);
|
||||
(
|
||||
json!({"event":"bootstrap","timestamp":Utc::now(),"data":data}),
|
||||
revision,
|
||||
|
||||
@@ -111,3 +111,16 @@ pub struct HomeAssistantSettingsView {
|
||||
pub flow_inputs: Vec<FlowSharedInput>,
|
||||
pub outdoor_assist_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SettingsSnapshot {
|
||||
pub application: ApplicationSettings,
|
||||
pub gree: GreeSettings,
|
||||
pub history: HistorySettings,
|
||||
pub influxdb: InfluxDbSettingsView,
|
||||
pub notifications: NotificationSettingsView,
|
||||
pub night: NightModeSettings,
|
||||
pub home_assistant: HomeAssistantSettingsView,
|
||||
pub debug: DebugSettings,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user