use crate::error::{ProtocolError, Result};
use std::collections::HashMap;
use std::marker::PhantomData;
pub mod decode;
pub mod dpt_type;
pub mod payload;
pub mod unit;
pub mod view;
pub mod dpt1; pub mod dpt10; pub mod dpt11; pub mod dpt12; pub mod dpt13; pub mod dpt14; pub mod dpt16; pub mod dpt17; pub mod dpt18; pub mod dpt19; pub mod dpt20; pub mod dpt232; pub mod dpt235; pub mod dpt242; pub mod dpt251;
pub mod dpt29; pub mod dpt3; pub mod dpt5; pub mod dpt6; pub mod dpt7; pub mod dpt8; pub mod dpt9;
#[cfg(test)]
mod tests;
pub use decode::{DecodedTelegram, decode_telegram};
pub use dpt_type::DptType;
pub use dpt1::*;
pub use dpt3::*;
pub use dpt5::*;
pub use dpt6::*;
pub use dpt7::*;
pub use dpt8::*;
pub use dpt9::*;
pub use dpt10::*;
pub use dpt11::*;
pub use dpt12::*;
pub use dpt13::*;
pub use dpt14::*;
pub use dpt16::*;
pub use dpt17::*;
pub use dpt18::*;
pub use dpt19::*;
pub use dpt20::*;
pub use dpt29::*;
pub use dpt232::*;
pub use dpt235::*;
pub use dpt242::*;
pub use dpt251::*;
pub use payload::DptPayload;
pub use unit::Unit;
pub use view::{
BoolView, ControlView, DateTimeView, DateView, DptView, EnumView, Float2ByteView,
Float4ByteView, I8View, I16View, I32View, I64View, RgbView, RgbwView, SceneView, StrView,
TimeView, U8View, U16View, U32View, XyyView,
};
#[macro_export]
macro_rules! dpt_alias {
($name:ident, $main:literal, $sub:literal, $base:ty, $value_type:literal, $unit:expr, $ha_class:expr) => {
#[derive(Debug, Clone, PartialEq)]
pub struct $name($base);
impl $name {
pub fn new(value: <$base as $crate::protocol::dpt::DptInnerType>::InnerType) -> Self {
Self(<$base as $crate::protocol::dpt::DptInnerType>::new(value))
}
pub fn value(&self) -> <$base as $crate::protocol::dpt::DptInnerType>::InnerType {
self.0.clone().into_inner()
}
}
impl $crate::protocol::dpt::DptValue for $name {
const DPT_NUMBER: &'static str = concat!(stringify!($main), ".", stringify!($sub));
const VALUE_TYPE: &'static str = $value_type;
const UNIT: Option<&'static str> = $unit;
const HA_DEVICE_CLASS: Option<&'static str> = $ha_class;
const BYTE_LENGTH: usize = <$base>::BYTE_LENGTH;
fn from_bytes(bytes: &[u8]) -> $crate::error::Result<Self> {
let base_value = <$base>::from_bytes(bytes)?;
Ok(Self(base_value))
}
fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
fn validate(&self) -> $crate::error::Result<()> {
self.0.validate()
}
fn value_range() -> (f64, f64) {
<$base>::value_range()
}
}
};
}
pub trait DptInnerType {
type InnerType;
fn new(value: Self::InnerType) -> Self;
fn into_inner(self) -> Self::InnerType;
}
pub trait DptValue: Sized + Send + Sync + std::fmt::Debug + Clone + PartialEq + 'static {
const DPT_NUMBER: &'static str;
const VALUE_TYPE: &'static str;
const UNIT: Option<&'static str> = None;
const HA_DEVICE_CLASS: Option<&'static str> = None;
const BYTE_LENGTH: usize;
fn from_bytes(bytes: &[u8]) -> Result<Self>;
fn as_bytes(&self) -> &[u8];
fn validate(&self) -> Result<()>;
fn value_range() -> (f64, f64);
fn decode(bytes: &[u8]) -> Result<Self> {
Self::from_bytes(bytes)
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(self.as_bytes().to_vec())
}
#[must_use]
fn metadata() -> DptMetadata {
DptMetadata {
dpt_number: Self::DPT_NUMBER,
value_type: Self::VALUE_TYPE,
unit: Self::UNIT,
ha_device_class: Self::HA_DEVICE_CLASS,
byte_length: Self::BYTE_LENGTH,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Dpt<T: DptValue> {
value: T,
}
impl<T: DptValue> Dpt<T> {
pub fn new(value: T) -> Result<Self> {
value.validate()?;
Ok(Self { value })
}
pub fn value(&self) -> &T {
&self.value
}
pub fn encode(&self) -> Result<Vec<u8>> {
Ok(self.value.as_bytes().to_vec())
}
pub fn as_bytes(&self) -> &[u8] {
self.value.as_bytes()
}
pub fn decode(bytes: &[u8]) -> Result<Self> {
let value = T::decode(bytes)?;
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DptMetadata {
pub dpt_number: &'static str,
pub value_type: &'static str,
pub unit: Option<&'static str>,
pub ha_device_class: Option<&'static str>,
pub byte_length: usize,
}
pub struct DptRegistry {
decoders: HashMap<&'static str, Box<dyn DptDecoder>>,
value_types: HashMap<&'static str, &'static str>,
}
pub trait DptDecoder: Send + Sync {
fn decode_bytes(&self, data: &[u8]) -> Result<Box<dyn std::any::Any + Send + Sync>>;
fn encode_any(&self, value: &dyn std::any::Any) -> Result<Vec<u8>>;
fn dpt_number(&self) -> &'static str;
fn metadata(&self) -> DptMetadata;
fn clone_decoder(&self) -> Box<dyn DptDecoder>;
}
#[derive(Clone)]
pub struct GenericDptDecoder<T: DptValue> {
_phantom: PhantomData<T>,
}
impl<T: DptValue> GenericDptDecoder<T> {
#[must_use]
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<T: DptValue> Default for GenericDptDecoder<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: DptValue> DptDecoder for GenericDptDecoder<T> {
fn decode_bytes(&self, data: &[u8]) -> Result<Box<dyn std::any::Any + Send + Sync>> {
let value = T::decode(data)?;
Ok(Box::new(value))
}
fn encode_any(&self, value: &dyn std::any::Any) -> Result<Vec<u8>> {
let typed_value = value
.downcast_ref::<T>()
.ok_or_else(|| ProtocolError::DptError {
dpt_type: T::DPT_NUMBER.to_string(),
details: "Type mismatch in encoding".to_string(),
})?;
Ok(typed_value.as_bytes().to_vec())
}
fn dpt_number(&self) -> &'static str {
T::DPT_NUMBER
}
fn metadata(&self) -> DptMetadata {
T::metadata()
}
fn clone_decoder(&self) -> Box<dyn DptDecoder> {
Box::new(self.clone())
}
}
impl DptRegistry {
#[must_use]
pub fn new() -> Self {
let mut registry = Self {
decoders: HashMap::new(),
value_types: HashMap::new(),
};
registry.register::<DPTSwitch>();
registry.register::<DPTBool>();
registry.register::<DPTEnable>();
registry.register::<DPTRamp>();
registry.register::<DPTAlarm>();
registry.register::<DPTBinaryValue>();
registry.register::<DPTStep>();
registry.register::<DPTUpDown>();
registry.register::<DPTOpenClose>();
registry.register::<DPTStart>();
registry.register::<DPTState>();
registry.register::<DPTInvert>();
registry.register::<DPTDimSendStyle>();
registry.register::<DPTInputSource>();
registry.register::<DPTReset>();
registry.register::<DPTAck>();
registry.register::<DPTTrigger>();
registry.register::<DPTOccupancy>();
registry.register::<DPTWindowDoor>();
registry.register::<DPTLogicalFunction>();
registry.register::<DPTSceneAB>();
registry.register::<DPTShutterBlindsMode>();
registry.register::<DPTDayNight>();
registry.register::<DPTHeatCool>();
registry.register::<DPTConsumerProducer>();
registry.register::<DPTEnergyDirection>();
registry.register::<ControlDimming>();
registry.register::<ControlBlinds>();
registry.register::<DPTScaling>();
registry.register::<DPTAngle>();
registry.register::<DPTPercentU8>();
registry.register::<DPTDecimalFactor>();
registry.register::<DPTTariff>();
registry.register::<DPTValue1Ucount>();
registry.register::<DPTPercentV8>();
registry.register::<DPTValue1Count>();
registry.register::<DPT2Ucount>();
registry.register::<DPTTimePeriodMsec>();
registry.register::<DPTTimePeriod10Msec>();
registry.register::<DPTTimePeriod100Msec>();
registry.register::<DPTTimePeriodSec>();
registry.register::<DPTTimePeriodMin>();
registry.register::<DPTTimePeriodHrs>();
registry.register::<DPTPropDataType>();
registry.register::<DPTLengthMm>();
registry.register::<DPTUElCurrentmA>();
registry.register::<DPTBrightness>();
registry.register::<DPTColorTemperature>();
registry.register::<DPTValue2Count>();
registry.register::<DPTDeltaTimeMsec>();
registry.register::<DPTDeltaTime10Msec>();
registry.register::<DPTDeltaTime100Msec>();
registry.register::<DPTDeltaTimeSec>();
registry.register::<DPTDeltaTimeMin>();
registry.register::<DPTDeltaTimeHrs>();
registry.register::<DPTPercentV16>();
registry.register::<DPTRotationAngle>();
registry.register::<DPTLengthM>();
registry.register::<DPTTemperature>();
registry.register::<DPTTemperatureDifference2Byte>();
registry.register::<DPTTemperatureA>();
registry.register::<DPTLux>();
registry.register::<DPTWsp>();
registry.register::<DPTPressure2Byte>();
registry.register::<DPTHumidity>();
registry.register::<DPTPartsPerMillion>();
registry.register::<DPTAirFlow>();
registry.register::<DPTTime1>();
registry.register::<DPTTime2>();
registry.register::<DPTVoltage>();
registry.register::<DPTCurrent>();
registry.register::<DPTPowerDensity>();
registry.register::<DPTKelvinPerPercent>();
registry.register::<DPTPower2Byte>();
registry.register::<DPTVolumeFlow>();
registry.register::<DPTRainAmount>();
registry.register::<DPTTemperatureF>();
registry.register::<DPTWspKmh>();
registry.register::<DPTAbsoluteHumidity>();
registry.register::<DPTConcentrationUGM3>();
registry.register::<DPTEnthalpy>();
registry.register::<DPTValue4Ucount>();
registry.register::<DPTLongTimePeriodSec>();
registry.register::<DPTLongTimePeriodMin>();
registry.register::<DPTLongTimePeriodHrs>();
registry.register::<DPTVolumeLiquidLitre>();
registry.register::<DPTVolumeM3>();
registry.register::<DPTValue4Count>();
registry.register::<DPTFlowRateM3H>();
registry.register::<DPTActiveEnergy>();
registry.register::<DPTApparantEnergy>();
registry.register::<DPTReactiveEnergy>();
registry.register::<DPTActiveEnergykWh>();
registry.register::<DPTApparantEnergykVAh>();
registry.register::<DPTReactiveEnergykVARh>();
registry.register::<DPTActiveEnergyMWh>();
registry.register::<DPTLongDeltaTimeSec>();
registry.register::<DPTDeltaVolumeLiquidLitre>();
registry.register::<DPTDeltaVolumeM3>();
registry.register::<DPTAcceleration>();
registry.register::<DPTAccelerationAngular>();
registry.register::<DPTActivationEnergy>();
registry.register::<DPTActivity>();
registry.register::<DPTMol>();
registry.register::<DPTAmplitude>();
registry.register::<DPTAngleRad>();
registry.register::<DPTAngleDeg>();
registry.register::<DPTAngularMomentum>();
registry.register::<DPTAngularVelocity>();
registry.register::<DPTArea>();
registry.register::<DPTCapacitance>();
registry.register::<DPTChargeDensitySurface>();
registry.register::<DPTChargeDensityVolume>();
registry.register::<DPTCompressibility>();
registry.register::<DPTConductance>();
registry.register::<DPTElectricalConductivity>();
registry.register::<DPTDensity>();
registry.register::<DPTElectricCharge>();
registry.register::<DPTElectricCurrent>();
registry.register::<DPTElectricCurrentDensity>();
registry.register::<DPTElectricDipoleMoment>();
registry.register::<DPTElectricDisplacement>();
registry.register::<DPTElectricFieldStrength>();
registry.register::<DPTElectricFlux>();
registry.register::<DPTElectricFluxDensity>();
registry.register::<DPTElectricPolarization>();
registry.register::<DPTElectricPotential>();
registry.register::<DPTElectricPotentialDifference>();
registry.register::<DPTElectromagneticMoment>();
registry.register::<DPTElectromotiveForce>();
registry.register::<DPTEnergy>();
registry.register::<DPTForce>();
registry.register::<DPTFrequency>();
registry.register::<DPTAngularFrequency>();
registry.register::<DPTHeatCapacity>();
registry.register::<DPTHeatFlowRate>();
registry.register::<DPTHeatQuantity>();
registry.register::<DPTImpedance>();
registry.register::<DPTLength>();
registry.register::<DPTLightQuantity>();
registry.register::<DPTLuminance>();
registry.register::<DPTLuminousFlux>();
registry.register::<DPTLuminousIntensity>();
registry.register::<DPTMagneticFieldStrength>();
registry.register::<DPTMagneticFlux>();
registry.register::<DPTMagneticFluxDensity>();
registry.register::<DPTMagneticMoment>();
registry.register::<DPTMagneticPolarization>();
registry.register::<DPTMagnetization>();
registry.register::<DPTMagnetomotiveForce>();
registry.register::<DPTMass>();
registry.register::<DPTMassFlux>();
registry.register::<DPTMomentum>();
registry.register::<DPTPhaseAngleRad>();
registry.register::<DPTPhaseAngleDeg>();
registry.register::<DPTPower>();
registry.register::<DPTPowerFactor>();
registry.register::<DPTPressure>();
registry.register::<DPTReactance>();
registry.register::<DPTResistance>();
registry.register::<DPTResistivity>();
registry.register::<DPTSelfInductance>();
registry.register::<DPTSolidAngle>();
registry.register::<DPTSoundIntensity>();
registry.register::<DPTSpeed>();
registry.register::<DPTStress>();
registry.register::<DPTSurfaceTension>();
registry.register::<DPTCommonTemperature>();
registry.register::<DPTAbsoluteTemperature>();
registry.register::<DPTTemperatureDifference>();
registry.register::<DPTThermalCapacity>();
registry.register::<DPTThermalConductivity>();
registry.register::<DPTThermoelectricPower>();
registry.register::<DPTTimeSeconds>();
registry.register::<DPTTorque>();
registry.register::<DPTVolume>();
registry.register::<DPTVolumeFlux>();
registry.register::<DPTWeight>();
registry.register::<DPTWork>();
registry.register::<DPTApparentPower>();
registry.register::<DPTVolumeFluxMeter>();
registry.register::<DPTVolumeFluxLs>();
registry.register::<TimeOfDay>();
registry.register::<Date>();
registry.register::<StringAscii>();
registry.register::<StringLatin1>();
registry.register::<DPTSceneNumber>();
registry.register::<SceneControl>();
registry.register::<DateTime>();
registry.register::<DPTHVACMode>();
registry.register::<DPTHVACContrMode>();
registry.register::<DPTActiveEnergy8Byte>();
registry.register::<DPTApparantEnergy8Byte>();
registry.register::<DPTReactiveEnergy8Byte>();
registry.register::<ColorRGB>();
registry.register::<TariffActiveEnergy>();
registry.register::<ColorXYY>();
registry.register::<ColorRGBW>();
registry
}
pub fn register<T: DptValue>(&mut self) {
let decoder = GenericDptDecoder::<T>::new();
self.decoders.insert(T::DPT_NUMBER, Box::new(decoder));
self.value_types.insert(T::VALUE_TYPE, T::DPT_NUMBER);
}
pub fn dpt_number_for_value_type(&self, value_type: &str) -> Result<&'static str> {
self.value_types.get(value_type).copied().ok_or_else(|| {
ProtocolError::DptError {
dpt_type: value_type.to_string(),
details: "Unknown DPT value type".to_string(),
}
.into()
})
}
pub fn metadata(&self, dpt: &str) -> Result<DptMetadata> {
let decoder = self
.decoders
.get(dpt)
.ok_or_else(|| ProtocolError::DptError {
dpt_type: dpt.to_string(),
details: "Unknown DPT type".to_string(),
})?;
Ok(decoder.metadata())
}
pub fn decode(&self, dpt: &str, data: &[u8]) -> Result<Box<dyn std::any::Any + Send + Sync>> {
let decoder = self
.decoders
.get(dpt)
.ok_or_else(|| ProtocolError::DptError {
dpt_type: dpt.to_string(),
details: "Unknown DPT type".to_string(),
})?;
decoder.decode_bytes(data)
}
pub fn encode(&self, dpt: &str, value: &dyn std::any::Any) -> Result<Vec<u8>> {
let decoder = self
.decoders
.get(dpt)
.ok_or_else(|| ProtocolError::DptError {
dpt_type: dpt.to_string(),
details: "Unknown DPT type".to_string(),
})?;
decoder.encode_any(value)
}
}
impl Default for DptRegistry {
fn default() -> Self {
Self::new()
}
}