klafs_api/
models.rs

1use serde::{Deserialize, Serialize};
2use serde_repr::{Deserialize_repr, Serialize_repr};
3
4/// Information about a registered sauna
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct SaunaInfo {
7    /// Unique identifier (UUID) for the sauna
8    pub id: String,
9    /// Display name of the sauna
10    pub name: String,
11}
12
13/// Operating mode for the sauna
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(try_from = "u8", into = "u8")]
16pub enum SaunaMode {
17    /// Traditional sauna mode (dry heat, 10-100°C)
18    Sauna = 1,
19    /// Sanarium mode (humid heat, 40-75°C)
20    Sanarium = 2,
21    /// Infrared mode
22    Infrared = 3,
23}
24
25impl TryFrom<u8> for SaunaMode {
26    type Error = String;
27
28    fn try_from(value: u8) -> Result<Self, Self::Error> {
29        match value {
30            1 => Ok(SaunaMode::Sauna),
31            2 => Ok(SaunaMode::Sanarium),
32            3 => Ok(SaunaMode::Infrared),
33            _ => Err(format!("Invalid sauna mode: {}", value)),
34        }
35    }
36}
37
38impl From<SaunaMode> for u8 {
39    fn from(mode: SaunaMode) -> Self {
40        mode as u8
41    }
42}
43
44impl std::fmt::Display for SaunaMode {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        match self {
47            SaunaMode::Sauna => write!(f, "Sauna"),
48            SaunaMode::Sanarium => write!(f, "Sanarium"),
49            SaunaMode::Infrared => write!(f, "Infrared"),
50        }
51    }
52}
53
54/// Status code returned by the API
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum StatusCode {
57    /// Sauna is off
58    Off,
59    /// Sauna is heating up
60    HeatingUp,
61    /// Sauna is ready for use
62    Ready,
63    /// Sauna is in standby/idle
64    Standby,
65    /// Unknown status
66    Unknown(i32),
67}
68
69impl From<i32> for StatusCode {
70    fn from(value: i32) -> Self {
71        match value {
72            0 => StatusCode::Off,
73            1 => StatusCode::HeatingUp,
74            2 => StatusCode::Ready,
75            3 => StatusCode::Standby,
76            other => StatusCode::Unknown(other),
77        }
78    }
79}
80
81impl std::fmt::Display for StatusCode {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            StatusCode::Off => write!(f, "Off"),
85            StatusCode::HeatingUp => write!(f, "Heating Up"),
86            StatusCode::Ready => write!(f, "Ready"),
87            StatusCode::Standby => write!(f, "Standby"),
88            StatusCode::Unknown(code) => write!(f, "Unknown ({})", code),
89        }
90    }
91}
92
93/// Operational status of the sauna (from opStatus field)
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize_repr, Deserialize_repr)]
95#[repr(i32)]
96pub enum OpStatus {
97    /// Sauna is off
98    Off = 0,
99    /// Sauna is scheduled, waiting for start time
100    Scheduled = 1,
101    /// Sauna is heating up
102    Heating = 2,
103    /// Sauna is ready for use
104    Ready = 3,
105}
106
107impl std::fmt::Display for OpStatus {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        match self {
110            OpStatus::Off => write!(f, "Off"),
111            OpStatus::Scheduled => write!(f, "Scheduled"),
112            OpStatus::Heating => write!(f, "Heating"),
113            OpStatus::Ready => write!(f, "Ready"),
114        }
115    }
116}
117
118/// Full sauna status as returned by the GetSaunaStatus API
119#[derive(Debug, Clone, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase")]
121pub struct SaunaStatus {
122    /// Unique identifier for this sauna
123    pub sauna_id: String,
124
125    /// Whether traditional sauna mode is selected
126    pub sauna_selected: bool,
127
128    /// Whether sanarium mode is selected
129    pub sanarium_selected: bool,
130
131    /// Whether infrared mode is selected
132    pub ir_selected: bool,
133
134    /// Target temperature for sauna mode (10-100°C)
135    pub selected_sauna_temperature: i32,
136
137    /// Target temperature for sanarium mode (40-75°C)
138    pub selected_sanarium_temperature: i32,
139
140    /// Target temperature for infrared mode
141    pub selected_ir_temperature: i32,
142
143    /// Humidity level for sanarium mode (1-10)
144    pub selected_hum_level: i32,
145
146    /// Infrared level (1-10)
147    pub selected_ir_level: i32,
148
149    /// Scheduled start hour (0-23)
150    pub selected_hour: i32,
151
152    /// Scheduled start minute (0-59)
153    pub selected_minute: i32,
154
155    /// Whether the sauna is connected to the network
156    pub is_connected: bool,
157
158    /// Whether the sauna is currently powered on
159    pub is_powered_on: bool,
160
161    /// Whether the sauna is ready for use (reached target temperature)
162    pub is_ready_for_use: bool,
163
164    /// Current measured temperature in °C
165    pub current_temperature: i32,
166
167    /// Current measured humidity percentage
168    pub current_humidity: i32,
169
170    /// Status code (see StatusCode enum)
171    pub status_code: i32,
172
173    /// Optional status message
174    pub status_message: Option<String>,
175
176    /// Whether to show remaining bathing time
177    pub show_remaining_bathing_time: bool,
178
179    /// Remaining bathing hours
180    pub remaining_bathing_hours: i32,
181
182    /// Remaining bathing minutes
183    pub remaining_bathing_minutes: i32,
184
185    /// Whether bathing time is selected
186    pub bathing_time_selected: bool,
187
188    /// Selected bathing time hours
189    pub selected_bathing_time_hours: i32,
190
191    /// Selected bathing time minutes
192    pub selected_bathing_time_minutes: i32,
193
194    /// Whether a scheduled time is set
195    pub time_selected: bool,
196
197    /// Current humidity status indicator
198    pub current_humidity_status: i32,
199
200    /// Current temperature status indicator
201    pub current_temperature_status: i32,
202
203    /// Currently selected temperature (based on mode)
204    pub selected_temperature: i32,
205
206    /// Currently selected mode (1=Sauna, 2=Sanarium, etc.)
207    pub selected_mode: i32,
208
209    /// Operation status (Off, Scheduled, Active)
210    pub op_status: OpStatus,
211
212    /// Light is on
213    pub light_is_on: bool,
214
215    /// Light brightness
216    pub light_brightness: i32,
217
218    /// Color light is on
219    pub color_light_is_on: bool,
220
221    /// Color light brightness
222    pub color_light_brightness: i32,
223
224    /// Color light color
225    pub color_light_color: i32,
226
227    /// Sunset is on
228    pub sunset_is_on: bool,
229
230    /// Sunset brightness
231    pub sunset_brightness: i32,
232
233    /// Whether login is required
234    #[serde(default)]
235    pub login_required: bool,
236
237    /// Whether request was successful
238    #[serde(default = "default_true")]
239    pub success: bool,
240
241    /// Error message if any
242    #[serde(default)]
243    pub error_message: String,
244
245    /// Error message header
246    #[serde(default)]
247    pub error_message_header: String,
248}
249
250fn default_true() -> bool {
251    true
252}
253
254impl SaunaStatus {
255    /// Get the currently selected mode
256    pub fn current_mode(&self) -> Option<SaunaMode> {
257        if self.sauna_selected {
258            Some(SaunaMode::Sauna)
259        } else if self.sanarium_selected {
260            Some(SaunaMode::Sanarium)
261        } else if self.ir_selected {
262            Some(SaunaMode::Infrared)
263        } else {
264            None
265        }
266    }
267
268    /// Get the target temperature for the currently selected mode
269    pub fn target_temperature(&self) -> i32 {
270        if self.sauna_selected {
271            self.selected_sauna_temperature
272        } else if self.sanarium_selected {
273            self.selected_sanarium_temperature
274        } else if self.ir_selected {
275            self.selected_ir_temperature
276        } else {
277            self.selected_sauna_temperature
278        }
279    }
280
281    /// Get the status code as an enum
282    pub fn status(&self) -> StatusCode {
283        StatusCode::from(self.status_code)
284    }
285
286    /// Get remaining bathing time as a formatted string
287    pub fn remaining_time(&self) -> String {
288        format!(
289            "{}h {:02}m",
290            self.remaining_bathing_hours, self.remaining_bathing_minutes
291        )
292    }
293}
294
295/// Request body for StartCabin endpoint
296#[derive(Debug, Serialize)]
297pub(crate) struct PowerControlRequest {
298    pub id: String,
299    pub pin: String,
300    pub time_selected: bool,
301    pub sel_hour: i32,
302    pub sel_min: i32,
303}
304
305/// Request body for setting temperature
306#[derive(Debug, Serialize)]
307pub(crate) struct SetTemperatureRequest {
308    pub id: String,
309    pub temperature: i32,
310}
311
312/// Request body for setting humidity level
313#[derive(Debug, Serialize)]
314pub(crate) struct SetHumidityRequest {
315    pub id: String,
316    pub level: i32,
317}
318
319/// Request body for setting mode
320#[derive(Debug, Serialize)]
321pub(crate) struct SetModeRequest {
322    pub id: String,
323    pub selected_mode: u8,
324}
325
326/// Request body for SetSelectedTime endpoint (schedule without starting)
327#[derive(Debug, Serialize)]
328pub(crate) struct SetSelectedTimeRequest {
329    pub id: String,
330    pub time_set: bool,
331    pub hours: i32,
332    pub minutes: i32,
333}
334
335/// Request body for LightChange endpoint
336#[derive(Debug, Serialize)]
337pub(crate) struct LightChangeRequest {
338    pub id: String,
339    pub light_id: u8,
340    pub on_off: bool,
341    pub brightness: i32,
342    pub color: i32,
343}
344
345/// Light types for the LightChange endpoint
346#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347pub enum LightType {
348    /// Main cabin light
349    Main = 1,
350    /// Color light
351    Color = 2,
352    /// Sunset light
353    Sunset = 3,
354}
355
356impl From<LightType> for u8 {
357    fn from(light: LightType) -> Self {
358        light as u8
359    }
360}