/ web / src / api.rs
api.rs
  1  use serde::{Deserialize, Serialize};
  2  
  3  pub const DEFAULT_DEVICE_URL: &str = "http://ceratina.local";
  4  
  5  // ─── /api/system/device/status ──────────────────────────────────────────────
  6  
  7  #[derive(Debug, Clone, Deserialize)]
  8  pub struct DeviceStatusEnvelope {
  9      pub data: DeviceStatusData,
 10      pub time: String,
 11  }
 12  
 13  #[derive(Debug, Clone, Deserialize)]
 14  pub struct DeviceStatusData {
 15      pub device: DeviceIdentity,
 16      pub network: DeviceNetwork,
 17      pub runtime: DeviceRuntime,
 18      pub sleep: DeviceSleep,
 19      pub storage: DeviceStorage,
 20  }
 21  
 22  #[derive(Debug, Clone, Deserialize)]
 23  pub struct DeviceIdentity {
 24      pub chip_model: String,
 25      pub chip_cores: u32,
 26      pub chip_revision: u32,
 27  }
 28  
 29  #[derive(Debug, Clone, Deserialize)]
 30  pub struct DeviceNetwork {
 31      pub ipv4_address: String,
 32      pub wifi_rssi: i32,
 33  }
 34  
 35  #[derive(Debug, Clone, Deserialize)]
 36  pub struct DeviceRuntime {
 37      pub uptime: String,
 38      pub uptime_seconds: u64,
 39      #[serde(default)]
 40      pub temperature_celsius: f64,
 41      pub memory_heap_free: u64,
 42      #[serde(default)]
 43      pub memory_heap_total: u64,
 44      #[serde(default)]
 45      pub memory_heap_min_free: u64,
 46      #[serde(default)]
 47      pub memory_heap_max_alloc: u64,
 48      #[serde(default)]
 49      pub memory_psram_total: u64,
 50      #[serde(default)]
 51      pub memory_psram_free: u64,
 52  }
 53  
 54  #[derive(Debug, Clone, Deserialize)]
 55  pub struct DeviceStorage {
 56      pub location: String,
 57      pub total_bytes: u64,
 58      pub used_bytes: u64,
 59      pub free_bytes: u64,
 60  }
 61  
 62  impl DeviceStorage {
 63      pub fn percent_used(&self) -> f64 {
 64          if self.total_bytes == 0 {
 65              0.0
 66          } else {
 67              (self.used_bytes as f64 / self.total_bytes as f64) * 100.0
 68          }
 69      }
 70  }
 71  
 72  #[derive(Debug, Clone, Deserialize)]
 73  pub struct DeviceSleep {
 74      #[serde(default)]
 75      pub pending: bool,
 76      #[serde(default)]
 77      pub requested_duration_seconds: u64,
 78      #[serde(default)]
 79      pub wake_cause: String,
 80      #[serde(default)]
 81      pub timer_wakeup_enabled: bool,
 82      #[serde(default)]
 83      pub timer_wakeup_us: u64,
 84      #[serde(default)]
 85      pub enabled: bool,
 86      #[serde(default)]
 87      pub default_duration_seconds: u64,
 88  }
 89  
 90  #[derive(Debug, Clone, Deserialize)]
 91  pub struct SleepConfigResponse {
 92      pub ok: bool,
 93      pub data: SleepConfigData,
 94  }
 95  
 96  #[derive(Debug, Clone, Deserialize, Serialize)]
 97  pub struct SleepConfigData {
 98      pub enabled: bool,
 99      pub duration_seconds: u64,
100  }
101  
102  // ─── /api/cloudevents ───────────────────────────────────────────────────────
103  
104  #[derive(Debug, Clone, Deserialize, Serialize)]
105  pub struct CloudEvent {
106      pub id: String,
107      #[serde(rename = "type")]
108      pub event_type: String,
109      pub time: String,
110      pub data: serde_json::Value,
111  }
112  
113  // ─── /api/wireless/status ───────────────────────────────────────────────────
114  
115  #[derive(Debug, Clone, Deserialize)]
116  pub struct WirelessStatusResponse {
117      pub ok: bool,
118      pub data: WirelessStatusData,
119  }
120  
121  #[derive(Debug, Clone, Deserialize)]
122  pub struct WirelessStatusData {
123      pub connected: bool,
124      pub sta_ssid: String,
125      pub sta_ipv4: String,
126      pub wifi_rssi: i32,
127      pub ap_active: bool,
128      pub ap_ssid: String,
129      pub ap_ipv4: String,
130  }
131  
132  // ─── /api/wireless/actions/scan ─────────────────────────────────────────────
133  
134  #[derive(Debug, Clone, Deserialize)]
135  pub struct WifiScanResponse {
136      pub ok: bool,
137      pub data: WifiScanData,
138  }
139  
140  #[derive(Debug, Clone, Deserialize)]
141  pub struct WifiScanData {
142      pub scan_count: u32,
143      pub networks: Vec<WifiNetwork>,
144  }
145  
146  #[derive(Debug, Clone, Deserialize)]
147  pub struct WifiNetwork {
148      pub ssid: String,
149      pub rssi: i32,
150      pub channel: u8,
151      pub encryption: String,
152      pub open: bool,
153  }
154  
155  // ─── /api/filesystem/list ───────────────────────────────────────────────────
156  
157  #[derive(Debug, Clone, Deserialize, PartialEq)]
158  pub struct FileEntry {
159      pub name: String,
160      pub size: u64,
161  }
162  
163  // ─── CO2 control ────────────────────────────────────────────────────────────
164  
165  #[derive(Debug, Clone, Deserialize)]
166  pub struct Co2ConfigResponse {
167      pub ok: bool,
168      pub data: Co2ConfigData,
169  }
170  
171  #[derive(Debug, Clone, Deserialize)]
172  pub struct Co2ConfigData {
173      pub model: String,
174      pub measuring: bool,
175      pub measurement_interval_seconds: u16,
176      pub auto_calibration_enabled: bool,
177      pub temperature_offset_celsius: f64,
178      pub altitude_meters: u16,
179  }
180  
181  // ─── Utilities ──────────────────────────────────────────────────────────────
182  
183  pub fn format_file_size(bytes: u64) -> String {
184      if bytes < 1024 {
185          format!("{} B", bytes)
186      } else if bytes < 1024 * 1024 {
187          format!("{:.1} KB", bytes as f64 / 1024.0)
188      } else {
189          format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0))
190      }
191  }
192  
193  pub fn format_storage_pair(used: u64, total: u64) -> String {
194      let (used_val, total_val, unit) = if total < 1024 {
195          (used as f64, total as f64, "B")
196      } else if total < 1024 * 1024 {
197          (used as f64 / 1024.0, total as f64 / 1024.0, "KB")
198      } else {
199          (
200              used as f64 / (1024.0 * 1024.0),
201              total as f64 / (1024.0 * 1024.0),
202              "MB",
203          )
204      };
205      format!("{used_val:.1} / {total_val:.1} {unit}")
206  }