use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "lowercase")]
pub enum LightColor {
Red,
Yellow,
Green,
Blue,
White,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum LightMode {
Off,
On,
#[serde(alias = "solid")]
Solid,
Pattern1,
Pattern2,
Pattern3,
Pattern4,
Pattern5,
Pattern6,
#[serde(alias = "blink")]
Blink,
#[serde(alias = "flash")]
Flash,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum BuzzerPattern {
Off,
On,
#[serde(alias = "continuous")]
Continuous,
Sweep,
Intermittent,
#[serde(alias = "weak_attention", alias = "weak", alias = "chirp")]
WeakAttention,
#[serde(alias = "strong_attention", alias = "strong", alias = "alarm")]
StrongAttention,
#[serde(alias = "shining_star")]
ShiningStar,
#[serde(alias = "london_bridge")]
LondonBridge,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LightSettings {
pub color: String,
pub mode: LightMode,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct LightCommand {
pub color: LightColor,
pub mode: LightMode,
pub duration_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LightSequence {
pub commands: Vec<LightCommand>,
#[serde(default = "default_loop")]
pub loop_sequence: bool,
}
fn default_loop() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentLightState {
pub color: LightColor,
pub mode: LightMode,
pub active: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub remaining_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuzzerSettings {
pub pattern: BuzzerPattern,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuzzerPatternSettings {
pub pattern: Vec<BuzzerNote>,
pub repetitions: u8,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuzzerNote {
pub frequency_hz: u16,
pub duration_ms: u64,
pub pause_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LightState {
pub mode: LightMode,
pub active: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub remaining_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuzzerState {
pub active: bool,
pub pattern: Option<BuzzerPattern>,
pub volume: u8,
#[serde(skip_serializing_if = "Option::is_none")]
pub remaining_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BeaconStatus {
pub light: Option<CurrentLightState>,
pub sequence: Option<SequenceStatus>,
pub buzzer: BuzzerState,
pub connected: bool,
pub last_updated: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SequenceStatus {
pub commands: Vec<LightCommand>,
pub current_index: usize,
pub loop_sequence: bool,
pub active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestResult {
pub success: bool,
pub results: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiKeyQuery {
#[serde(rename = "apiKey")]
pub api_key: Option<String>,
}
impl Default for LightState {
fn default() -> Self {
Self {
mode: LightMode::Off,
active: false,
remaining_ms: None,
}
}
}
impl Default for BuzzerState {
fn default() -> Self {
Self {
active: false,
pattern: None,
volume: 50,
remaining_ms: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_light_color_serialization() {
let color = LightColor::Red;
let json = serde_json::to_string(&color).unwrap();
assert_eq!(json, r#""red""#);
let deserialized: LightColor = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, color);
}
#[test]
fn test_light_mode_serialization() {
let mode = LightMode::Flash;
let json = serde_json::to_string(&mode).unwrap();
assert_eq!(json, r#""flash""#);
let deserialized: LightMode = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, mode);
}
#[test]
fn test_light_settings_serialization() {
let settings = LightSettings {
color: "red".to_string(),
mode: LightMode::On,
duration_ms: Some(1000),
};
let json = serde_json::to_string(&settings).unwrap();
assert!(json.contains(r#""color":"red""#));
assert!(json.contains(r#""mode":"on""#));
assert!(json.contains(r#""duration_ms":1000"#));
let deserialized: LightSettings = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.color, settings.color);
assert_eq!(deserialized.mode, settings.mode);
assert_eq!(deserialized.duration_ms, settings.duration_ms);
}
#[test]
fn test_beacon_status_default() {
let status = BeaconStatus {
light: None,
sequence: None,
buzzer: BuzzerState::default(),
connected: true,
last_updated: "2025-01-01T00:00:00Z".to_string(),
};
assert!(!status.buzzer.active);
assert_eq!(status.buzzer.volume, 50);
}
#[test]
fn test_buzzer_pattern_serialization() {
let pattern = BuzzerPattern::StrongAttention;
let json = serde_json::to_string(&pattern).unwrap();
assert_eq!(json, r#""strong_attention""#);
let deserialized: BuzzerPattern = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, pattern);
let json_alias = r#""alarm""#;
let deserialized: BuzzerPattern = serde_json::from_str(json_alias).unwrap();
assert_eq!(deserialized, BuzzerPattern::StrongAttention);
}
#[test]
fn test_light_sequence() {
let sequence = LightSequence {
commands: vec![
LightCommand {
color: LightColor::Red,
mode: LightMode::On,
duration_ms: 1000,
},
LightCommand {
color: LightColor::Green,
mode: LightMode::Flash,
duration_ms: 2000,
},
],
loop_sequence: true,
};
let json = serde_json::to_string(&sequence).unwrap();
let deserialized: LightSequence = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.commands.len(), 2);
assert_eq!(deserialized.commands[0].color, LightColor::Red);
assert_eq!(deserialized.commands[1].mode, LightMode::Flash);
assert!(deserialized.loop_sequence);
}
}