ws_protocol.rs
1 use heapless::String as HeaplessString; 2 use serde::{Deserialize, Serialize}; 3 4 // ─── Client → Device ──────────────────────────────────────────────────────── 5 // 6 // serde_json_core doesn't support internally tagged enums, so we parse 7 // the "action" field manually and deserialize the payload separately. 8 9 #[derive(Deserialize)] 10 pub struct RawRequest<'a> { 11 pub action: &'a str, 12 #[serde(default)] 13 pub ssid: Option<&'a str>, 14 #[serde(default)] 15 pub password: Option<&'a str>, 16 #[serde(default)] 17 pub location: Option<&'a str>, 18 #[serde(default)] 19 pub path: Option<&'a str>, 20 } 21 22 pub enum DeviceRequest<'a> { 23 GetStatus, 24 GetCo2, 25 ScanWifi, 26 ConnectWifi { ssid: &'a str, password: &'a str }, 27 ListFiles { location: &'a str }, 28 DeleteFile { location: &'a str, path: &'a str }, 29 Unknown, 30 } 31 32 impl<'a> From<RawRequest<'a>> for DeviceRequest<'a> { 33 fn from(raw: RawRequest<'a>) -> Self { 34 match raw.action { 35 "get_status" => DeviceRequest::GetStatus, 36 "get_co2" => DeviceRequest::GetCo2, 37 "scan_wifi" => DeviceRequest::ScanWifi, 38 "connect_wifi" => DeviceRequest::ConnectWifi { 39 ssid: raw.ssid.unwrap_or(""), 40 password: raw.password.unwrap_or(""), 41 }, 42 "list_files" => DeviceRequest::ListFiles { 43 location: raw.location.unwrap_or("sd"), 44 }, 45 "delete_file" => DeviceRequest::DeleteFile { 46 location: raw.location.unwrap_or("sd"), 47 path: raw.path.unwrap_or(""), 48 }, 49 _ => DeviceRequest::Unknown, 50 } 51 } 52 } 53 54 // ─── Device → Client ──────────────────────────────────────────────────────── 55 56 #[derive(Serialize)] 57 #[serde(tag = "type")] 58 pub enum DeviceEvent<'a> { 59 #[serde(rename = "status")] 60 Status { 61 hostname: &'a str, 62 platform: &'a str, 63 uptime_seconds: u64, 64 heap_free: usize, 65 heap_used: usize, 66 sd_card_mb: u32, 67 }, 68 #[serde(rename = "co2")] 69 Co2 { 70 co2_ppm: f32, 71 temperature: f32, 72 humidity: f32, 73 model: &'a str, 74 ok: bool, 75 }, 76 #[serde(rename = "wifi_scan")] 77 WifiScan { 78 networks: &'a [WifiNetworkEntry], 79 }, 80 #[serde(rename = "wifi_status")] 81 WifiStatus { 82 connected: bool, 83 ssid: &'a str, 84 ipv4: HeaplessString<16>, 85 rssi: i32, 86 }, 87 #[serde(rename = "file_list")] 88 FileList { 89 location: &'a str, 90 files: &'a [FileEntryPayload], 91 }, 92 #[serde(rename = "error")] 93 Error { 94 message: &'a str, 95 }, 96 } 97 98 #[derive(Serialize)] 99 pub struct WifiNetworkEntry { 100 pub ssid: HeaplessString<32>, 101 pub rssi: i8, 102 pub channel: u8, 103 } 104 105 #[derive(Serialize)] 106 pub struct FileEntryPayload { 107 pub name: HeaplessString<32>, 108 pub size: u32, 109 }