/ firmware / src / services / ws_protocol.rs
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  }