v0.14.2-connectivity

This commit is contained in:
Mateusz Gruszczyński
2026-09-15 10:02:49 +02:00
parent 2fd7e47eb9
commit c3f6e773f8
41 changed files with 1295 additions and 108 deletions
+103
View File
@@ -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,
})))
}