v0.14.2-connectivity
This commit is contained in:
@@ -628,6 +628,9 @@ fn normalize_imported_runtime_settings(settings: &mut RuntimeSettings) -> Result
|
||||
settings.zone_interval_seconds = gree.zone_interval_seconds;
|
||||
settings.discovery_timeout_ms = gree.discovery_timeout_ms;
|
||||
settings.discovery_broadcast = gree.discovery_broadcast;
|
||||
settings.ping_metrics_enabled = gree.ping_metrics_enabled;
|
||||
settings.ping_interval_seconds = gree.ping_interval_seconds;
|
||||
settings.ping_sample_count = gree.ping_sample_count;
|
||||
settings.suppress_device_beep = gree.suppress_device_beep;
|
||||
settings.compressor_protection_enabled = gree.compressor_protection_enabled;
|
||||
settings.compressor_protection_seconds = gree.compressor_protection_seconds;
|
||||
@@ -637,6 +640,10 @@ fn normalize_imported_runtime_settings(settings: &mut RuntimeSettings) -> Result
|
||||
}
|
||||
settings.gree_cloud.polling_interval_seconds =
|
||||
settings.gree_cloud.polling_interval_seconds.clamp(30, 3600);
|
||||
settings.gree_cloud.connectivity_metrics_interval_seconds =
|
||||
settings.gree_cloud.connectivity_metrics_interval_seconds.clamp(30, 3600);
|
||||
settings.gree_cloud.connectivity_metrics_sample_count =
|
||||
settings.gree_cloud.connectivity_metrics_sample_count.clamp(1, 10);
|
||||
if settings.gree_cloud.account_id.trim().is_empty() {
|
||||
settings.gree_cloud.account_id = "default".into();
|
||||
}
|
||||
|
||||
@@ -775,3 +775,106 @@ async fn energy_history(
|
||||
"latest": latest,
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct NetworkHistoryQuery {
|
||||
target_id: Option<String>,
|
||||
hours: Option<i64>,
|
||||
limit: Option<u32>,
|
||||
}
|
||||
|
||||
async fn combined_network_history(
|
||||
state: &AppState,
|
||||
target_id: Option<&str>,
|
||||
since: chrono::DateTime<Utc>,
|
||||
bucket_seconds: i64,
|
||||
limit: u32,
|
||||
) -> Result<(Vec<NetworkReading>, String, Option<String>), AppError> {
|
||||
let influx = state.settings.read().await.influxdb.clone();
|
||||
let cutoff = Utc::now() - ChronoDuration::days(influx.history_threshold_days.max(1) as i64);
|
||||
if !influx.enabled || since >= cutoff {
|
||||
return Ok((
|
||||
state.db.list_network_history(target_id, since, bucket_seconds, limit)?,
|
||||
"sqlite".into(),
|
||||
None,
|
||||
));
|
||||
}
|
||||
let mut warning = None;
|
||||
let mut values = match influxdb::query_network(
|
||||
&state.http,
|
||||
&influx,
|
||||
target_id,
|
||||
since,
|
||||
cutoff,
|
||||
bucket_seconds,
|
||||
limit,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(rows) => rows,
|
||||
Err(err) => {
|
||||
warning = Some(err.to_string());
|
||||
state.log(
|
||||
"warn",
|
||||
"influx.query_error",
|
||||
"InfluxDB connectivity history query failed",
|
||||
json!({"target_id": target_id, "error": err.to_string()}),
|
||||
);
|
||||
state.db.list_network_history(target_id, since, bucket_seconds, limit)?
|
||||
}
|
||||
};
|
||||
if warning.is_none() {
|
||||
values.extend(state.db.list_network_history(target_id, cutoff, bucket_seconds, limit)?);
|
||||
}
|
||||
values.sort_by_key(|row| row.timestamp);
|
||||
trim_history(&mut values, limit);
|
||||
Ok((
|
||||
values,
|
||||
if warning.is_some() { "sqlite_fallback".into() } else { "influx+sqlite".into() },
|
||||
warning,
|
||||
))
|
||||
}
|
||||
|
||||
async fn network_history(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<NetworkHistoryQuery>,
|
||||
) -> Result<Json<Value>, AppError> {
|
||||
let hours = query.hours.unwrap_or(24).clamp(1, 24 * 3650);
|
||||
let since = Utc::now() - ChronoDuration::hours(hours);
|
||||
let bucket_seconds = history_bucket_seconds(hours);
|
||||
let limit = query.limit.unwrap_or(20_000).clamp(1, 50_000);
|
||||
let target_id = query
|
||||
.target_id
|
||||
.as_deref()
|
||||
.filter(|value| !value.is_empty() && *value != "all");
|
||||
let (readings, storage, warning) =
|
||||
combined_network_history(&state, target_id, since, bucket_seconds, limit).await?;
|
||||
|
||||
let settings = state.settings.read().await.clone();
|
||||
let mut targets = Vec::<Value>::new();
|
||||
for device in state.db.list_devices()? {
|
||||
if device.enabled && device.connection_type == ConnectionType::Local && !device.simulated {
|
||||
targets.push(json!({
|
||||
"id": device.id,
|
||||
"name": device.name,
|
||||
"kind": "device",
|
||||
"source": "local_udp",
|
||||
}));
|
||||
}
|
||||
}
|
||||
let has_rest = readings.iter().any(|row| row.target_id == "cloud:rest");
|
||||
let has_mqtt = readings.iter().any(|row| row.target_id == "cloud:mqtt");
|
||||
if (settings.gree_cloud.enabled && settings.gree_cloud.connectivity_metrics_enabled) || has_rest {
|
||||
targets.push(json!({"id":"cloud:rest","name":"GREE Cloud REST","kind":"cloud_service","source":"cloud_rest"}));
|
||||
}
|
||||
if (settings.gree_cloud.enabled && settings.gree_cloud.connectivity_metrics_enabled) || has_mqtt {
|
||||
targets.push(json!({"id":"cloud:mqtt","name":"GREE Cloud MQTT","kind":"cloud_service","source":"cloud_mqtt"}));
|
||||
}
|
||||
Ok(Json(json!({
|
||||
"readings": readings,
|
||||
"targets": targets,
|
||||
"bucket_seconds": bucket_seconds,
|
||||
"storage": storage,
|
||||
"storage_warning": warning,
|
||||
})))
|
||||
}
|
||||
|
||||
+21
-1
@@ -11,6 +11,9 @@ fn gree_settings(settings: &RuntimeSettings) -> GreeSettings {
|
||||
zone_interval_seconds: settings.zone_interval_seconds,
|
||||
discovery_timeout_ms: settings.discovery_timeout_ms,
|
||||
discovery_broadcast: settings.discovery_broadcast.clone(),
|
||||
ping_metrics_enabled: settings.ping_metrics_enabled,
|
||||
ping_interval_seconds: settings.ping_interval_seconds,
|
||||
ping_sample_count: settings.ping_sample_count,
|
||||
suppress_device_beep: settings.suppress_device_beep,
|
||||
compressor_protection_enabled: settings.compressor_protection_enabled,
|
||||
compressor_protection_seconds: settings.compressor_protection_seconds,
|
||||
@@ -24,6 +27,9 @@ fn gree_cloud_settings(settings: &RuntimeSettings) -> GreeCloudSettingsView {
|
||||
username: settings.gree_cloud.username.clone(),
|
||||
password_configured: !settings.gree_cloud.password.is_empty(),
|
||||
polling_interval_seconds: settings.gree_cloud.polling_interval_seconds,
|
||||
connectivity_metrics_enabled: settings.gree_cloud.connectivity_metrics_enabled,
|
||||
connectivity_metrics_interval_seconds: settings.gree_cloud.connectivity_metrics_interval_seconds,
|
||||
connectivity_metrics_sample_count: settings.gree_cloud.connectivity_metrics_sample_count,
|
||||
installation_id: settings.gree_cloud.installation_id.clone(),
|
||||
account_id: settings.gree_cloud.account_id.clone(),
|
||||
last_successful_contact: settings.gree_cloud.last_successful_contact,
|
||||
@@ -138,6 +144,8 @@ fn normalize_gree_settings(mut input: GreeSettings) -> Result<GreeSettings, AppE
|
||||
input.poll_interval_seconds = input.poll_interval_seconds.clamp(2, 3600);
|
||||
input.zone_interval_seconds = input.zone_interval_seconds.clamp(2, 3600);
|
||||
input.discovery_timeout_ms = input.discovery_timeout_ms.clamp(300, 30_000);
|
||||
input.ping_interval_seconds = input.ping_interval_seconds.clamp(10, 3600);
|
||||
input.ping_sample_count = input.ping_sample_count.clamp(1, 10);
|
||||
input.compressor_protection_seconds = input.compressor_protection_seconds.clamp(30, 1800);
|
||||
if !(input.discovery_broadcast.eq_ignore_ascii_case("auto")
|
||||
|| input
|
||||
@@ -201,6 +209,9 @@ async fn update_gree_settings(
|
||||
settings.zone_interval_seconds = input.zone_interval_seconds;
|
||||
settings.discovery_timeout_ms = input.discovery_timeout_ms;
|
||||
settings.discovery_broadcast = input.discovery_broadcast;
|
||||
settings.ping_metrics_enabled = input.ping_metrics_enabled;
|
||||
settings.ping_interval_seconds = input.ping_interval_seconds;
|
||||
settings.ping_sample_count = input.ping_sample_count;
|
||||
settings.suppress_device_beep = input.suppress_device_beep;
|
||||
settings.compressor_protection_enabled = input.compressor_protection_enabled;
|
||||
settings.compressor_protection_seconds = input.compressor_protection_seconds;
|
||||
@@ -216,7 +227,10 @@ async fn update_gree_settings(
|
||||
"GREE settings updated",
|
||||
json!({
|
||||
"compressor_protection_enabled": payload.compressor_protection_enabled,
|
||||
"compressor_protection_seconds": payload.compressor_protection_seconds
|
||||
"compressor_protection_seconds": payload.compressor_protection_seconds,
|
||||
"ping_metrics_enabled": payload.ping_metrics_enabled,
|
||||
"ping_interval_seconds": payload.ping_interval_seconds,
|
||||
"ping_sample_count": payload.ping_sample_count
|
||||
}),
|
||||
);
|
||||
state.broadcast("settings.gree.updated", serde_json::to_value(&payload)?);
|
||||
@@ -246,6 +260,9 @@ fn apply_gree_cloud_update(
|
||||
next.region = input.region.trim().to_string();
|
||||
next.username = input.username.trim().to_string();
|
||||
next.polling_interval_seconds = input.polling_interval_seconds.clamp(30, 3600);
|
||||
next.connectivity_metrics_enabled = input.connectivity_metrics_enabled;
|
||||
next.connectivity_metrics_interval_seconds = input.connectivity_metrics_interval_seconds.clamp(30, 3600);
|
||||
next.connectivity_metrics_sample_count = input.connectivity_metrics_sample_count.clamp(1, 10);
|
||||
if let Some(password) = input.password {
|
||||
next.password = password;
|
||||
}
|
||||
@@ -292,6 +309,9 @@ async fn update_gree_cloud_settings(
|
||||
"enabled": payload.enabled,
|
||||
"region": payload.region,
|
||||
"polling_interval_seconds": payload.polling_interval_seconds,
|
||||
"connectivity_metrics_enabled": payload.connectivity_metrics_enabled,
|
||||
"connectivity_metrics_interval_seconds": payload.connectivity_metrics_interval_seconds,
|
||||
"connectivity_metrics_sample_count": payload.connectivity_metrics_sample_count,
|
||||
"password_configured": payload.password_configured
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user