use std::collections::HashMap;
use crate::e173_types;
use super::{
command::{Command, CommandClass},
localization::DefinitionLocalization,
parameter::{Parameter, ParameterClass},
resource::{Resource, ResourceClass},
serializer::{Serializer, SerializerClass},
structure::{Structure, StructureClass},
};
e173_types! {
#[serde(rename_all = "camelCase")]
pub struct DeviceClass {
pub libraries: HashMap<String, String>,
#[serde(rename = "@description")]
pub description: String,
pub publish_date: String,
pub author: String,
pub info: DeviceClassInfo,
pub history: HashMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_library: Option<DeviceLibrary>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub parameters: HashMap<String, Parameter>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub structures: HashMap<String, Structure>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub serializers: HashMap<String, Serializer>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub resources: HashMap<String, Resource>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub commands: HashMap<String, Command>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub localizations: HashMap<String, DefinitionLocalization>,
}
pub struct DeviceClassInfo {
pub manufacturer: Manufacturer,
pub model: Model,
#[serde(skip_serializing_if = "Option::is_none")]
pub compatibility: Option<Compatibility>,
}
#[serde(rename_all = "camelCase")]
pub struct Manufacturer {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(
default,
with = "esta_id",
skip_serializing_if = "Option::is_none"
)]
#[cfg_attr(target_arch = "wasm32", tsify(type = "string"))]
pub esta_id: Option<u16>,
}
pub struct Model {
pub name: String,
pub category: Category,
pub subcategory: Subcategory,
}
#[serde(rename_all = "kebab-case")]
#[cfg_attr(target_arch = "wasm32", tsify(string_enum))]
pub enum Category {
Lighting,
Video,
Audio,
MachineryAutomation,
Atmosphere,
Effect,
Infrastructure,
Other
}
#[serde(rename_all = "kebab-case")]
#[cfg_attr(target_arch = "wasm32", tsify(string_enum))]
pub enum Subcategory {
FixedProfile,
FixedFresnel,
FixedPebbleConvex,
FixedWash,
FixedPar,
FixedStrip,
FixedOther,
MovingProfile,
MovingFresnel,
MovingPebbleConvex,
MovingWash,
MovingStrip,
MovingMirror,
MovingOther,
AccessoryScroller,
AccessoryGoboRotator,
AccessoryAnimation,
AccessoryIris,
AccessoryOther,
ArchitecturalSconce,
ArchitecturalDownlight,
ArchitecturalFlood,
ArchitecturalSpot,
ArchitecturalTracklight,
ArchitecturalWallWash,
ArchitecturalOther,
PracticalFloorLamp,
PracticalDeskLamp,
PracticalTableLamp,
PracticalPendant,
PracticalChandelier,
PracticalOther,
Controller,
MediaServer,
Projector,
Panel,
Camera,
Amplifier,
SpeakerLineArray,
SpeakerPointSource,
SpeakerColumn,
SpeakerHornLoaded,
SpeakerMonitor,
SpeakerStageWedge,
SpeakerIem,
SpeakerSubwoofer,
SpeakerCeiling,
SpeakerSurface,
SpeakerOther,
Processor,
Mixer,
Winch,
Hoist,
Drive,
Revolve,
LoadCell,
Estop,
Smoke,
Haze,
Pyro,
Fire,
Strobe,
Laser,
Water,
Snow,
Bubble,
Fan,
NetworkSwitch,
Router,
Security,
Gateway,
Wireless,
Splitter,
Management,
Other,
}
#[serde(rename_all = "camelCase")]
pub struct Compatibility {
#[serde(skip_serializing_if = "Option::is_none")]
pub firmware_versions: Option<Vec<String>>,
}
#[serde(rename_all = "camelCase")]
pub struct DeviceLibrary {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub parameter_classes: HashMap<String, ParameterClass>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub structure_classes: HashMap<String, StructureClass>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub serializer_classes: HashMap<String, SerializerClass>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub resource_classes: HashMap<String, ResourceClass>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub command_classes: HashMap<String, CommandClass>,
}
#[serde(rename_all = "camelCase")]
pub struct Device {
pub class: String,
pub version: String,
pub user_identifier: String,
#[serde(rename = "@friendlyName", skip_serializing_if = "Option::is_none")]
pub friendly_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unit_number: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub translate: Option<(f64, f64, f64)>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rotate: Option<(f64, f64, f64)>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transport: Option<DeviceTransport>,
#[serde(skip_serializing_if = "Option::is_none")]
pub static_values: Option<DeviceStaticValues>,
}
#[serde(rename_all = "camelCase")]
pub struct DeviceTransport {
pub methods: HashMap<String, DeviceTransportMethod>,
}
#[serde(rename_all = "camelCase")]
pub struct DeviceTransportMethod {
pub serializer: String,
pub address: String
}
#[serde(rename_all = "camelCase")]
pub struct DeviceOverlay {
pub base: Option<String>,
pub static_values: Option<DeviceStaticValues>
}
#[serde(rename_all = "camelCase")]
pub struct DeviceStaticValues {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
#[cfg_attr(target_arch = "wasm32", tsify(type = "Record<string, string | number | boolean>"))]
pub parameters: HashMap<String, serde_json::Value>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
#[cfg_attr(target_arch = "wasm32", tsify(type = "Record<string, string | number | boolean>"))]
pub structures: HashMap<String, serde_json::Value>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
#[cfg_attr(target_arch = "wasm32", tsify(type = "Record<string, string | number | boolean>"))]
pub serializers: HashMap<String, serde_json::Value>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub resources: HashMap<String, String>,
}
}
mod esta_id {
use serde::{Deserialize, Deserializer, Serializer};
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
where
D: Deserializer<'de>,
{
let val: Option<String> = Option::<String>::deserialize(deserializer)?;
if let Some(val) = val {
let result = u16::from_str_radix(&val, 16).map_err(serde::de::Error::custom)?;
return if result < 0x8000 {
Ok(Some(result))
} else {
Err(serde::de::Error::custom(format!(
"Invalid ESTA manufacturer ID {val}"
)))
};
}
Ok(None)
}
pub fn serialize<S>(value: &Option<u16>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(value) => {
let s = format!("{value:x}");
serializer.serialize_str(&s)
}
None => serializer.serialize_none(),
}
}
}