This commit is contained in:
sebseb7
2025-12-25 03:58:07 +01:00
parent 489e07d4e1
commit 4610d80c4f
13 changed files with 276 additions and 1237 deletions

View File

@@ -178,6 +178,7 @@ async fn discover_and_create_config(
server_url: server,
api_key: key,
poll_interval_secs: 60,
command_url: None,
devices,
};
@@ -408,6 +409,36 @@ async fn collect_device_data(device: &DeviceConfig) -> Vec<Reading> {
readings
}
// Switch a device on or off
async fn switch_device(device: &DeviceConfig, turn_on: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = ApiClient::new(&device.tapo_email, &device.tapo_password);
match device.device_type.as_str() {
"P110" | "P115" => {
let plug = client.p110(&device.ip).await?;
if turn_on {
plug.on().await?;
} else {
plug.off().await?;
}
}
"P100" | "P105" => {
let plug = client.p100(&device.ip).await?;
if turn_on {
plug.on().await?;
} else {
plug.off().await?;
}
}
_ => {
return Err(format!("Unknown device type: {}", device.device_type).into());
}
}
info!("[Switch] Device {} turned {}", device.name, if turn_on { "ON" } else { "OFF" });
Ok(())
}
async fn run_agent(config: Config) -> Result<(), Box<dyn std::error::Error>> {
use tokio::sync::mpsc;
@@ -449,6 +480,9 @@ async fn run_agent(config: Config) -> Result<(), Box<dyn std::error::Error>> {
}
});
// Clone devices for command handling in main loop
let devices_for_commands = config.devices.clone();
// Connection and sending loop
let mut reconnect_delay = Duration::from_secs(1);
let max_reconnect_delay = Duration::from_secs(60);
@@ -512,6 +546,35 @@ async fn run_agent(config: Config) -> Result<(), Box<dyn std::error::Error>> {
// Handle incoming WebSocket messages
msg = read.next() => {
match msg {
Some(Ok(Message::Text(text))) => {
// Handle incoming commands from server
if let Ok(cmd) = serde_json::from_str::<serde_json::Value>(&text) {
if cmd.get("type").and_then(|v| v.as_str()) == Some("command") {
let device_name = cmd.get("device").and_then(|v| v.as_str()).unwrap_or("");
let action = cmd.get("action").and_then(|v| v.as_str()).unwrap_or("");
let value = cmd.get("value").and_then(|v| v.as_i64()).unwrap_or(0);
info!("[Command] Received: device={}, action={}, value={}", device_name, action, value);
// Find matching device in our config
if let Some(device) = devices_for_commands.iter().find(|d| d.name == device_name) {
if action == "set_state" {
let turn_on = value > 0;
info!("[Command] Switching {} {}", device_name, if turn_on { "ON" } else { "OFF" });
let device_clone = device.clone();
tokio::spawn(async move {
if let Err(e) = switch_device(&device_clone, turn_on).await {
error!("[Command] Failed to switch {}: {}", device_clone.name, e);
}
});
}
} else {
warn!("[Command] Unknown device: {}", device_name);
}
}
}
}
Some(Ok(Message::Ping(data))) => {
let _ = write.send(Message::Pong(data)).await;
}