first commit
This commit is contained in:
730
crates/z2m/src/api.rs
Normal file
730
crates/z2m/src/api.rs
Normal file
@@ -0,0 +1,730 @@
|
||||
#![allow(clippy::struct_excessive_bools)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RawMessage {
|
||||
pub topic: String,
|
||||
pub payload: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(tag = "topic", content = "payload")]
|
||||
pub enum Message {
|
||||
#[serde(rename = "bridge/info")]
|
||||
BridgeInfo(Box<BridgeInfo>),
|
||||
|
||||
#[serde(rename = "bridge/state")]
|
||||
BridgeState(Value),
|
||||
|
||||
#[serde(rename = "bridge/event")]
|
||||
BridgeEvent(BridgeEvent),
|
||||
|
||||
#[serde(rename = "bridge/devices")]
|
||||
BridgeDevices(BridgeDevices),
|
||||
|
||||
#[serde(rename = "bridge/groups")]
|
||||
BridgeGroups(BridgeGroups),
|
||||
|
||||
#[serde(rename = "bridge/logging")]
|
||||
BridgeLogging(BridgeLogging),
|
||||
|
||||
#[serde(rename = "bridge/definitions")]
|
||||
BridgeDefinitions(Value),
|
||||
|
||||
#[serde(rename = "bridge/extensions")]
|
||||
BridgeExtensions(Value),
|
||||
|
||||
#[serde(rename = "bridge/converters")]
|
||||
BridgeConverters(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/options")]
|
||||
BridgeOptions(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/touchlink/scan")]
|
||||
BridgeTouchlinkScan(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/permit_join")]
|
||||
BridgePermitJoin(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/networkmap")]
|
||||
BridgeNetworkmap(Value),
|
||||
|
||||
#[serde(rename = "bridge/config")]
|
||||
BridgeConfig(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/group/add")]
|
||||
BridgeResponseGroupAdd(Response<GroupAdd>),
|
||||
|
||||
#[serde(rename = "bridge/response/group/remove")]
|
||||
BridgeResponseGroupRemove(Response<GroupRemove>),
|
||||
|
||||
#[serde(rename = "bridge/response/group/rename")]
|
||||
BridgeResponseGroupRename(Response<GroupRename>),
|
||||
|
||||
#[serde(rename = "bridge/response/group/options")]
|
||||
BridgeResponseGroupOptions(Response<GroupOptions>),
|
||||
|
||||
#[serde(rename = "bridge/response/group/members/add")]
|
||||
BridgeGroupMembersAdd(Response<GroupMemberChange>),
|
||||
|
||||
#[serde(rename = "bridge/response/group/members/remove")]
|
||||
BridgeGroupMembersRemove(Response<GroupMemberChange>),
|
||||
|
||||
#[serde(rename = "bridge/response/device/remove")]
|
||||
BridgeDeviceRemove(Response<DeviceRemoveResponse>),
|
||||
|
||||
#[serde(rename = "bridge/response/device/options")]
|
||||
BridgeDeviceOptions(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/device/configure_reporting")]
|
||||
BridgeDeviceConfigureReporting(Value),
|
||||
|
||||
#[serde(rename = "bridge/response/device/ota_update/check")]
|
||||
BridgeDeviceOtaUpdateCheck(Value),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||
pub enum Endpoint {
|
||||
#[default]
|
||||
Default,
|
||||
#[serde(untagged)]
|
||||
Name(String),
|
||||
#[serde(untagged)]
|
||||
Number(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct GroupMemberChange {
|
||||
pub device: String,
|
||||
pub group: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub endpoint: Option<Endpoint>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub skip_disable_reporting: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct PermitJoin {
|
||||
pub time: u32,
|
||||
pub device: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DeviceRemove {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct DeviceRemoveResponse {
|
||||
pub id: String,
|
||||
pub block: bool,
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "status", rename_all = "lowercase")]
|
||||
pub enum Response<T> {
|
||||
Ok {
|
||||
data: T,
|
||||
#[serde(default)]
|
||||
transaction: Option<Value>,
|
||||
},
|
||||
Error {
|
||||
error: Value,
|
||||
#[serde(default)]
|
||||
transaction: Option<Value>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GroupAdd {
|
||||
pub id: Option<u32>,
|
||||
pub friendly_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GroupRemove {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GroupMemberAddRemove {
|
||||
pub device: String,
|
||||
pub endpoint: u8,
|
||||
pub group: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GroupRename {
|
||||
pub from: String,
|
||||
pub to: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GroupOptions {
|
||||
pub from: Value,
|
||||
pub to: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceRename {
|
||||
pub from: String,
|
||||
pub to: String,
|
||||
#[serde(default)]
|
||||
pub homeassistant_rename: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Hash, Debug, Copy)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Availability {
|
||||
Online,
|
||||
Offline,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Hash)]
|
||||
#[serde(transparent)]
|
||||
pub struct IeeeAddress(#[serde(deserialize_with = "ieee_address")] u64);
|
||||
|
||||
impl Debug for IeeeAddress {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "IeeeAddress({:016x})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IeeeAddress {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "0x{:016x}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn ieee_address<'de, D>(deserializer: D) -> Result<u64, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
let s: &str = Deserialize::deserialize(deserializer)?;
|
||||
let num = u64::from_str_radix(s.trim_start_matches("0x"), 16).map_err(Error::custom)?;
|
||||
Ok(num)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum BridgeOnlineState {
|
||||
Online,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct BridgeState {
|
||||
pub state: BridgeOnlineState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct BridgeEvent {
|
||||
/* FIXME: needs proper mapping */
|
||||
/* See: <zigbee2mqtt>/lib/extension/bridge.ts */
|
||||
pub data: Value,
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct BridgeLogging {
|
||||
pub level: String,
|
||||
pub message: String,
|
||||
pub topic: Option<String>,
|
||||
}
|
||||
|
||||
type BridgeGroups = Vec<Group>;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Group {
|
||||
pub friendly_name: String,
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
pub id: u32,
|
||||
pub members: Vec<GroupMember>,
|
||||
pub scenes: Vec<Scene>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct GroupMember {
|
||||
pub endpoint: u32,
|
||||
pub ieee_address: IeeeAddress,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct EndpointLink {
|
||||
pub endpoint: u32,
|
||||
pub ieee_address: IeeeAddress,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct GroupLink {
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Scene {
|
||||
pub id: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BridgeInfo {
|
||||
pub commit: String,
|
||||
pub config: Config,
|
||||
pub config_schema: BridgeConfigSchema,
|
||||
pub coordinator: Coordinator,
|
||||
pub log_level: String,
|
||||
pub network: Network,
|
||||
pub permit_join: bool,
|
||||
pub restart_required: bool,
|
||||
pub version: String,
|
||||
pub zigbee_herdsman: Version,
|
||||
pub zigbee_herdsman_converters: Version,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BridgeConfigSchema {
|
||||
pub definitions: Value,
|
||||
#[serde(default)]
|
||||
pub required: Vec<String>,
|
||||
pub properties: Value,
|
||||
#[serde(rename = "type")]
|
||||
pub config_type: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub advanced: ConfigAdvanced,
|
||||
#[serde(default)]
|
||||
pub availability: Value,
|
||||
#[serde(default)]
|
||||
pub version: Value,
|
||||
pub blocklist: Vec<Option<Value>>,
|
||||
pub device_options: Value,
|
||||
pub devices: HashMap<String, Value>,
|
||||
#[serde(default)]
|
||||
pub external_converters: Vec<Option<Value>>,
|
||||
pub frontend: Value,
|
||||
pub groups: HashMap<String, GroupValue>,
|
||||
#[serde(with = "crate::serde_util::struct_or_false")]
|
||||
pub homeassistant: Option<ConfigHomeassistant>,
|
||||
pub map_options: Value,
|
||||
pub mqtt: Value,
|
||||
pub ota: Value,
|
||||
pub passlist: Vec<Option<Value>>,
|
||||
pub serial: ConfigSerial,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Version {
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Network {
|
||||
pub channel: i64,
|
||||
pub extended_pan_id: Value,
|
||||
pub pan_id: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Coordinator {
|
||||
pub ieee_address: IeeeAddress,
|
||||
/* stict parsing disabled for now, format too volatile between versions */
|
||||
/* pub meta: CoordinatorMeta, */
|
||||
pub meta: Value,
|
||||
#[serde(rename = "type")]
|
||||
pub coordinator_type: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigAdvanced {
|
||||
pub adapter_concurrent: Option<Value>,
|
||||
pub adapter_delay: Option<Value>,
|
||||
pub cache_state: bool,
|
||||
pub cache_state_persistent: bool,
|
||||
pub cache_state_send_on_startup: bool,
|
||||
pub channel: i64,
|
||||
pub elapsed: bool,
|
||||
pub ext_pan_id: Vec<i64>,
|
||||
pub homeassistant_legacy_entity_attributes: Option<bool>,
|
||||
pub last_seen: String,
|
||||
pub log_debug_namespace_ignore: String,
|
||||
pub log_debug_to_mqtt_frontend: bool,
|
||||
pub log_directory: String,
|
||||
pub log_file: String,
|
||||
pub log_level: String,
|
||||
pub log_namespaced_levels: Value,
|
||||
pub log_output: Vec<String>,
|
||||
pub log_rotation: bool,
|
||||
pub log_symlink_current: bool,
|
||||
pub log_syslog: Value,
|
||||
pub output: String,
|
||||
pub pan_id: i64,
|
||||
pub timestamp_format: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CoordinatorMeta {
|
||||
pub build: i64,
|
||||
pub ezsp: i64,
|
||||
pub major: i64,
|
||||
pub minor: i64,
|
||||
pub patch: i64,
|
||||
pub revision: String,
|
||||
pub special: i64,
|
||||
#[serde(rename = "type")]
|
||||
pub meta_type: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigSerial {
|
||||
pub adapter: Option<String>,
|
||||
pub disable_led: bool,
|
||||
#[serde(default)]
|
||||
pub port: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConfigHomeassistant {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub experimental_event_entities: Option<Value>,
|
||||
#[serde(default)]
|
||||
pub legacy_action_sensor: Option<Value>,
|
||||
pub discovery_topic: String,
|
||||
pub status_topic: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GroupValue {
|
||||
#[serde(default)]
|
||||
pub devices: Vec<String>,
|
||||
pub friendly_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub enum PowerSource {
|
||||
#[serde(rename = "Unknown")]
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
#[serde(rename = "Mains (single phase)")]
|
||||
MainsSinglePhase = 1,
|
||||
#[serde(rename = "Mains (3 phase)")]
|
||||
MainsThreePhase = 2,
|
||||
#[serde(rename = "Battery")]
|
||||
Battery = 3,
|
||||
#[serde(rename = "DC Source")]
|
||||
DcSource = 4,
|
||||
#[serde(rename = "Emergency mains constantly powered")]
|
||||
EmergencyMainsConstantly = 5,
|
||||
#[serde(rename = "Emergency mains and transfer switch")]
|
||||
EmergencyMainsAndTransferSwitch = 6,
|
||||
}
|
||||
|
||||
pub type BridgeDevices = Vec<Device>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum DeviceType {
|
||||
Coordinator,
|
||||
Router,
|
||||
EndDevice,
|
||||
Unknown,
|
||||
GreenPower,
|
||||
}
|
||||
|
||||
#[allow(clippy::pub_underscore_fields)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Device {
|
||||
pub description: Option<String>,
|
||||
pub date_code: Option<String>,
|
||||
pub definition: Option<DeviceDefinition>,
|
||||
pub disabled: bool,
|
||||
pub endpoints: HashMap<String, DeviceEndpoint>,
|
||||
pub friendly_name: String,
|
||||
pub ieee_address: IeeeAddress,
|
||||
pub interview_completed: bool,
|
||||
pub interviewing: bool,
|
||||
pub manufacturer: Option<String>,
|
||||
pub model_id: Option<String>,
|
||||
pub network_address: u16,
|
||||
#[serde(default)]
|
||||
pub power_source: PowerSource,
|
||||
pub software_build_id: Option<String>,
|
||||
pub supported: Option<bool>,
|
||||
#[serde(rename = "type")]
|
||||
pub device_type: DeviceType,
|
||||
|
||||
/* all other fields */
|
||||
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||
#[serde(default, flatten)]
|
||||
pub __: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
#[must_use]
|
||||
pub fn exposes(&self) -> &[Expose] {
|
||||
self.definition.as_ref().map_or(&[], |def| &def.exposes)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn expose_light(&self) -> Option<&ExposeLight> {
|
||||
self.exposes().iter().find_map(|exp| {
|
||||
if let Expose::Light(light) = exp {
|
||||
Some(light)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn expose_gradient(&self) -> Option<&ExposeList> {
|
||||
self.exposes().iter().find_map(|exp| {
|
||||
if let Expose::List(grad) = exp {
|
||||
if grad
|
||||
.base
|
||||
.property
|
||||
.as_ref()
|
||||
.is_some_and(|prop| prop == "gradient")
|
||||
{
|
||||
Some(grad)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn expose_action(&self) -> bool {
|
||||
self.exposes().iter().any(|exp| {
|
||||
if let Expose::Enum(ExposeEnum { base, .. }) = exp {
|
||||
base.name.as_deref() == Some("action")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceDefinition {
|
||||
pub model: String,
|
||||
pub vendor: String,
|
||||
pub description: String,
|
||||
pub exposes: Vec<Expose>,
|
||||
pub supports_ota: bool,
|
||||
pub options: Vec<Expose>,
|
||||
#[serde(default)]
|
||||
pub icon: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum Expose {
|
||||
Binary(ExposeBinary),
|
||||
Composite(ExposeComposite),
|
||||
Enum(ExposeEnum),
|
||||
Light(ExposeLight),
|
||||
Lock(ExposeLock),
|
||||
Numeric(ExposeNumeric),
|
||||
Switch(ExposeSwitch),
|
||||
List(ExposeList),
|
||||
|
||||
/* FIXME: Not modelled yet */
|
||||
Text(ExposeGeneric),
|
||||
Cover(ExposeGeneric),
|
||||
Fan(ExposeGeneric),
|
||||
Climate(ExposeGeneric),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeGeneric {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
#[serde(flatten)]
|
||||
pub other: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ExposeCategory {
|
||||
Config,
|
||||
Diagnostic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeBase {
|
||||
pub name: Option<String>,
|
||||
pub label: Option<String>,
|
||||
#[serde(default)]
|
||||
pub access: u8,
|
||||
pub endpoint: Option<String>,
|
||||
pub property: Option<String>,
|
||||
pub description: Option<String>,
|
||||
#[serde(default)]
|
||||
pub features: Vec<Expose>,
|
||||
pub category: Option<ExposeCategory>,
|
||||
}
|
||||
|
||||
impl Expose {
|
||||
#[must_use]
|
||||
pub const fn base(&self) -> &ExposeBase {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match self {
|
||||
Self::Binary(exp) => &exp.base,
|
||||
Self::Composite(exp) => &exp.base,
|
||||
Self::Enum(exp) => &exp.base,
|
||||
Self::Light(exp) => &exp.base,
|
||||
Self::List(exp) => &exp.base,
|
||||
Self::Lock(exp) => &exp.base,
|
||||
Self::Numeric(exp) => &exp.base,
|
||||
Self::Switch(exp) => &exp.base,
|
||||
Self::Text(exp) => &exp.base,
|
||||
Self::Cover(exp) => &exp.base,
|
||||
Self::Fan(exp) => &exp.base,
|
||||
Self::Climate(exp) => &exp.base,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.base().name.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeBinary {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
pub value_off: Value,
|
||||
pub value_on: Value,
|
||||
pub value_toggle: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeComposite {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
// FIXME
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeEnum {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
pub values: Vec<Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeLight {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeLock {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
}
|
||||
|
||||
impl ExposeLight {
|
||||
#[must_use]
|
||||
pub fn feature(&self, name: &str) -> Option<&Expose> {
|
||||
self.base
|
||||
.features
|
||||
.iter()
|
||||
.find(|exp| exp.name() == Some(name))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeList {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
pub item_type: Box<Expose>,
|
||||
#[serde(default)]
|
||||
pub length_min: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub length_max: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeNumeric {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
|
||||
pub unit: Option<String>,
|
||||
pub value_max: Option<f64>,
|
||||
pub value_min: Option<f64>,
|
||||
pub value_step: Option<f64>,
|
||||
|
||||
#[serde(default)]
|
||||
pub presets: Vec<Preset>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExposeSwitch {
|
||||
#[serde(flatten)]
|
||||
pub base: ExposeBase,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceEndpoint {
|
||||
pub bindings: Vec<DeviceEndpointBinding>,
|
||||
pub configured_reportings: Vec<DeviceEndpointConfiguredReporting>,
|
||||
pub clusters: DeviceEndpointClusters,
|
||||
pub scenes: Vec<Scene>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceEndpointConfiguredReporting {
|
||||
pub attribute: Value,
|
||||
pub cluster: String,
|
||||
pub maximum_report_interval: i64,
|
||||
pub minimum_report_interval: i64,
|
||||
#[serde(default)]
|
||||
pub reportable_change: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Preset {
|
||||
pub description: String,
|
||||
pub name: String,
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceEndpointBinding {
|
||||
pub cluster: String,
|
||||
pub target: DeviceEndpointBindingTarget,
|
||||
}
|
||||
|
||||
// NOTE: definition diverges from z2m, but is more strict
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum DeviceEndpointBindingTarget {
|
||||
Group(GroupLink),
|
||||
Endpoint(EndpointLink),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceEndpointClusters {
|
||||
pub input: Vec<String>,
|
||||
pub output: Vec<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user