use super::RpcHandler; use crate::mesh; use anyhow::Result; use tracing::info; impl RpcHandler { /// mesh.status — Get mesh radio status, device info, and peer count. pub(super) async fn handle_mesh_status(&self) -> Result { let service = self.mesh_service.read().await; if let Some(svc) = service.as_ref() { let status = svc.status().await; Ok(serde_json::to_value(status)?) } else { // No service running — return basic config + device detection let config = mesh::load_config(&self.config.data_dir).await?; let devices = mesh::detect_devices().await; Ok(serde_json::json!({ "enabled": config.enabled, "device_connected": false, "device_type": "unknown", "device_path": config.device_path, "channel_name": config.channel_name.unwrap_or_else(|| "archipelago".to_string()), "detected_devices": devices, "peer_count": 0, "messages_sent": 0, "messages_received": 0, })) } } /// mesh.peers — List discovered mesh peers. pub(super) async fn handle_mesh_peers(&self) -> Result { let service = self.mesh_service.read().await; if let Some(svc) = service.as_ref() { let peers = svc.peers().await; Ok(serde_json::json!({ "peers": peers, "count": peers.len(), })) } else { Ok(serde_json::json!({ "peers": [], "count": 0, })) } } /// mesh.messages — Get recent mesh message history. pub(super) async fn handle_mesh_messages( &self, params: Option, ) -> Result { let limit = params .as_ref() .and_then(|p| p.get("limit")) .and_then(|v| v.as_u64()) .map(|n| n as usize); let service = self.mesh_service.read().await; if let Some(svc) = service.as_ref() { let messages = svc.messages(limit).await; Ok(serde_json::json!({ "messages": messages, "count": messages.len(), })) } else { Ok(serde_json::json!({ "messages": [], "count": 0, })) } } /// mesh.send — Send an encrypted message to a mesh peer. pub(super) async fn handle_mesh_send( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let contact_id = params .get("contact_id") .and_then(|v| v.as_u64()) .ok_or_else(|| anyhow::anyhow!("Missing contact_id"))? as u32; let message = params .get("message") .and_then(|v| v.as_str()) .ok_or_else(|| anyhow::anyhow!("Missing message"))?; if message.is_empty() { anyhow::bail!("Message cannot be empty"); } let service = self.mesh_service.read().await; let svc = service .as_ref() .ok_or_else(|| anyhow::anyhow!("Mesh service not running. Enable mesh first."))?; let msg = svc.send_message(contact_id, message).await?; info!(contact_id, encrypted = msg.encrypted, "Sent mesh message"); Ok(serde_json::json!({ "sent": true, "message_id": msg.id, "encrypted": msg.encrypted, })) } /// mesh.broadcast — Broadcast our node identity over mesh. pub(super) async fn handle_mesh_broadcast(&self) -> Result { let service = self.mesh_service.read().await; let svc = service .as_ref() .ok_or_else(|| anyhow::anyhow!("Mesh service not running. Enable mesh first."))?; svc.broadcast_identity().await?; info!("Broadcast identity over mesh"); Ok(serde_json::json!({ "broadcast": true })) } /// mesh.configure — Enable/disable mesh and set device path. pub(super) async fn handle_mesh_configure( &self, params: Option, ) -> Result { let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?; let mut config = mesh::load_config(&self.config.data_dir).await?; if let Some(enabled) = params.get("enabled").and_then(|v| v.as_bool()) { config.enabled = enabled; } if let Some(device) = params.get("device_path").and_then(|v| v.as_str()) { config.device_path = Some(device.to_string()); } if let Some(channel) = params.get("channel_name").and_then(|v| v.as_str()) { config.channel_name = Some(channel.to_string()); } if let Some(broadcast) = params.get("broadcast_identity").and_then(|v| v.as_bool()) { config.broadcast_identity = broadcast; } if let Some(name) = params.get("advert_name").and_then(|v| v.as_str()) { config.advert_name = Some(name.to_string()); } mesh::save_config(&self.config.data_dir, &config).await?; // If we have a running service, update its config let mut service = self.mesh_service.write().await; if let Some(svc) = service.as_mut() { svc.configure(config.clone()).await?; } info!("Mesh config updated"); Ok(serde_json::json!({ "configured": true, "enabled": config.enabled, "device_path": config.device_path, })) } }