dmx512_rdm_protocol/rdm/
parameter.rs

1use super::{RdmError, SubDeviceId};
2use core::{
3    fmt,
4    net::{Ipv4Addr, Ipv6Addr},
5    result::Result,
6};
7
8#[cfg(not(feature = "alloc"))]
9use core::str::FromStr;
10#[cfg(not(feature = "alloc"))]
11use heapless::{String, Vec};
12
13#[cfg(feature = "alloc")]
14pub fn decode_string_bytes(bytes: &[u8]) -> Result<String, RdmError> {
15    let utf8 = String::from_utf8_lossy(bytes);
16
17    if utf8.contains(char::from(0)) {
18        Ok(utf8.split_once(char::from(0)).unwrap().0.to_string())
19    } else {
20        Ok(utf8.to_string())
21    }
22}
23
24#[cfg(not(feature = "alloc"))]
25pub fn decode_string_bytes<const N: usize>(bytes: &[u8]) -> Result<String<N>, RdmError> {
26    let utf8 = String::<N>::from_utf8(Vec::<u8, N>::from_slice(bytes).unwrap())?;
27
28    if utf8.contains(char::from(0)) {
29        Ok(String::<N>::from_str(utf8.split_once(char::from(0)).unwrap().0).unwrap())
30    } else {
31        Ok(utf8)
32    }
33}
34
35#[non_exhaustive]
36#[derive(Copy, Clone, Debug, PartialEq)]
37pub enum ParameterId {
38    // E1.20
39    DiscUniqueBranch,
40    DiscMute,
41    DiscUnMute,
42    ProxiedDevices,
43    ProxiedDeviceCount,
44    CommsStatus,
45    QueuedMessage,
46    StatusMessages,
47    StatusIdDescription,
48    ClearStatusId,
49    SubDeviceIdStatusReportThreshold,
50    SupportedParameters,
51    ParameterDescription,
52    DeviceInfo,
53    ProductDetailIdList,
54    DeviceModelDescription,
55    ManufacturerLabel,
56    DeviceLabel,
57    FactoryDefaults,
58    LanguageCapabilities,
59    Language,
60    SoftwareVersionLabel,
61    BootSoftwareVersionId,
62    BootSoftwareVersionLabel,
63    DmxPersonality,
64    DmxPersonalityDescription,
65    DmxStartAddress,
66    SlotInfo,
67    SlotDescription,
68    DefaultSlotValue,
69    SensorDefinition,
70    SensorValue,
71    RecordSensors,
72    DeviceHours,
73    LampHours,
74    LampStrikes,
75    LampState,
76    LampOnMode,
77    DevicePowerCycles,
78    DisplayInvert,
79    DisplayLevel,
80    PanInvert,
81    TiltInvert,
82    PanTiltSwap,
83    RealTimeClock,
84    IdentifyDevice,
85    ResetDevice,
86    PowerState,
87    PerformSelfTest,
88    SelfTestDescription,
89    CapturePreset,
90    PresetPlayback,
91    // E1.37-1
92    DmxBlockAddress,
93    DmxFailMode,
94    DmxStartupMode,
95    DimmerInfo,
96    MinimumLevel,
97    MaximumLevel,
98    Curve,
99    CurveDescription,
100    OutputResponseTime,
101    OutputResponseTimeDescription,
102    ModulationFrequency,
103    ModulationFrequencyDescription,
104    BurnIn,
105    LockPin,
106    LockState,
107    LockStateDescription,
108    IdentifyMode,
109    PresetInfo,
110    PresetStatus,
111    PresetMergeMode,
112    PowerOnSelfTest,
113    // E1.37-2
114    ListInterfaces,
115    InterfaceLabel,
116    InterfaceHardwareAddressType1,
117    IpV4DhcpMode,
118    IpV4ZeroConfMode,
119    IpV4CurrentAddress,
120    IpV4StaticAddress,
121    InterfaceRenewDhcp,
122    InterfaceReleaseDhcp,
123    InterfaceApplyConfiguration,
124    IpV4DefaultRoute,
125    DnsIpV4NameServer,
126    DnsHostName,
127    DnsDomainName,
128    // E1.37-7
129    EndpointList,
130    EndpointListChange,
131    IdentifyEndpoint,
132    EndpointToUniverse,
133    EndpointMode,
134    EndpointLabel,
135    RdmTrafficEnable,
136    DiscoveryState,
137    BackgroundDiscovery,
138    EndpointTiming,
139    EndpointTimingDescription,
140    EndpointResponders,
141    EndpointResponderListChange,
142    BindingControlFields,
143    BackgroundQueuedStatusPolicy,
144    BackgroundQueuedStatusPolicyDescription,
145    // E1.33
146    ComponentScope,
147    SearchDomain,
148    TcpCommsStatus,
149    BrokerStatus,
150    ManufacturerSpecific(u16),
151    Unsupported(u16),
152}
153
154impl From<u16> for ParameterId {
155    fn from(value: u16) -> Self {
156        match value {
157            // E1.20
158            0x0001 => Self::DiscUniqueBranch,
159            0x0002 => Self::DiscMute,
160            0x0003 => Self::DiscUnMute,
161            0x0010 => Self::ProxiedDevices,
162            0x0011 => Self::ProxiedDeviceCount,
163            0x0015 => Self::CommsStatus,
164            0x0020 => Self::QueuedMessage,
165            0x0030 => Self::StatusMessages,
166            0x0031 => Self::StatusIdDescription,
167            0x0032 => Self::ClearStatusId,
168            0x0033 => Self::SubDeviceIdStatusReportThreshold,
169            0x0050 => Self::SupportedParameters,
170            0x0051 => Self::ParameterDescription,
171            0x0060 => Self::DeviceInfo,
172            0x0070 => Self::ProductDetailIdList,
173            0x0080 => Self::DeviceModelDescription,
174            0x0081 => Self::ManufacturerLabel,
175            0x0082 => Self::DeviceLabel,
176            0x0090 => Self::FactoryDefaults,
177            0x00a0 => Self::LanguageCapabilities,
178            0x00b0 => Self::Language,
179            0x00c0 => Self::SoftwareVersionLabel,
180            0x00c1 => Self::BootSoftwareVersionId,
181            0x00c2 => Self::BootSoftwareVersionLabel,
182            0x00e0 => Self::DmxPersonality,
183            0x00e1 => Self::DmxPersonalityDescription,
184            0x00f0 => Self::DmxStartAddress,
185            0x0120 => Self::SlotInfo,
186            0x0121 => Self::SlotDescription,
187            0x0122 => Self::DefaultSlotValue,
188            0x0200 => Self::SensorDefinition,
189            0x0201 => Self::SensorValue,
190            0x0202 => Self::RecordSensors,
191            0x0400 => Self::DeviceHours,
192            0x0401 => Self::LampHours,
193            0x0402 => Self::LampStrikes,
194            0x0403 => Self::LampState,
195            0x0404 => Self::LampOnMode,
196            0x0405 => Self::DevicePowerCycles,
197            0x0500 => Self::DisplayInvert,
198            0x0501 => Self::DisplayLevel,
199            0x0600 => Self::PanInvert,
200            0x0601 => Self::TiltInvert,
201            0x0602 => Self::PanTiltSwap,
202            0x0603 => Self::RealTimeClock,
203            0x1000 => Self::IdentifyDevice,
204            0x1001 => Self::ResetDevice,
205            0x1010 => Self::PowerState,
206            0x1020 => Self::PerformSelfTest,
207            0x1021 => Self::SelfTestDescription,
208            0x1030 => Self::CapturePreset,
209            0x1031 => Self::PresetPlayback,
210            // E1.37-1
211            0x0140 => Self::DmxBlockAddress,
212            0x0141 => Self::DmxFailMode,
213            0x0142 => Self::DmxStartupMode,
214            0x0340 => Self::DimmerInfo,
215            0x0341 => Self::MinimumLevel,
216            0x0342 => Self::MaximumLevel,
217            0x0343 => Self::Curve,
218            0x0344 => Self::CurveDescription,
219            0x0345 => Self::OutputResponseTime,
220            0x0346 => Self::OutputResponseTimeDescription,
221            0x0347 => Self::ModulationFrequency,
222            0x0348 => Self::ModulationFrequencyDescription,
223            0x0440 => Self::BurnIn,
224            0x0640 => Self::LockPin,
225            0x0641 => Self::LockState,
226            0x0642 => Self::LockStateDescription,
227            0x1040 => Self::IdentifyMode,
228            0x1041 => Self::PresetInfo,
229            0x1042 => Self::PresetStatus,
230            0x1043 => Self::PresetMergeMode,
231            0x1044 => Self::PowerOnSelfTest,
232            // E1.37-2
233            0x0700 => Self::ListInterfaces,
234            0x0701 => Self::InterfaceLabel,
235            0x0702 => Self::InterfaceHardwareAddressType1,
236            0x0703 => Self::IpV4DhcpMode,
237            0x0704 => Self::IpV4ZeroConfMode,
238            0x0705 => Self::IpV4CurrentAddress,
239            0x0706 => Self::IpV4StaticAddress,
240            0x0707 => Self::InterfaceRenewDhcp,
241            0x0708 => Self::InterfaceReleaseDhcp,
242            0x0709 => Self::InterfaceApplyConfiguration,
243            0x070a => Self::IpV4DefaultRoute,
244            0x070b => Self::DnsIpV4NameServer,
245            0x070c => Self::DnsHostName,
246            0x070d => Self::DnsDomainName,
247            // E1.37-7
248            0x0900 => Self::EndpointList,
249            0x0901 => Self::EndpointListChange,
250            0x0902 => Self::IdentifyEndpoint,
251            0x0903 => Self::EndpointToUniverse,
252            0x0904 => Self::EndpointMode,
253            0x0905 => Self::EndpointLabel,
254            0x0906 => Self::RdmTrafficEnable,
255            0x0907 => Self::DiscoveryState,
256            0x0908 => Self::BackgroundDiscovery,
257            0x0909 => Self::EndpointTiming,
258            0x090a => Self::EndpointTimingDescription,
259            0x090b => Self::EndpointResponders,
260            0x090c => Self::EndpointResponderListChange,
261            0x090d => Self::BindingControlFields,
262            0x090e => Self::BackgroundQueuedStatusPolicy,
263            0x090f => Self::BackgroundQueuedStatusPolicyDescription,
264            // E1.33
265            0x8000 => Self::ComponentScope,
266            0x8001 => Self::SearchDomain,
267            0x8002 => Self::TcpCommsStatus,
268            0x8003 => Self::BrokerStatus,
269            n if (0x8000..=0xffdf).contains(&n) => Self::ManufacturerSpecific(n),
270            n => Self::Unsupported(n),
271        }
272    }
273}
274
275impl From<ParameterId> for u16 {
276    fn from(value: ParameterId) -> Self {
277        match value {
278            // E1.20
279            ParameterId::DiscUniqueBranch => 0x0001,
280            ParameterId::DiscMute => 0x0002,
281            ParameterId::DiscUnMute => 0x0003,
282            ParameterId::ProxiedDevices => 0x0010,
283            ParameterId::ProxiedDeviceCount => 0x0011,
284            ParameterId::CommsStatus => 0x0015,
285            ParameterId::QueuedMessage => 0x0020,
286            ParameterId::StatusMessages => 0x0030,
287            ParameterId::StatusIdDescription => 0x0031,
288            ParameterId::ClearStatusId => 0x0032,
289            ParameterId::SubDeviceIdStatusReportThreshold => 0x0033,
290            ParameterId::SupportedParameters => 0x0050,
291            ParameterId::ParameterDescription => 0x0051,
292            ParameterId::DeviceInfo => 0x0060,
293            ParameterId::ProductDetailIdList => 0x0070,
294            ParameterId::DeviceModelDescription => 0x0080,
295            ParameterId::ManufacturerLabel => 0x0081,
296            ParameterId::DeviceLabel => 0x0082,
297            ParameterId::FactoryDefaults => 0x0090,
298            ParameterId::LanguageCapabilities => 0x00a0,
299            ParameterId::Language => 0x00b0,
300            ParameterId::SoftwareVersionLabel => 0x00c0,
301            ParameterId::BootSoftwareVersionId => 0x00c1,
302            ParameterId::BootSoftwareVersionLabel => 0x00c2,
303            ParameterId::DmxPersonality => 0x00e0,
304            ParameterId::DmxPersonalityDescription => 0x00e1,
305            ParameterId::DmxStartAddress => 0x00f0,
306            ParameterId::SlotInfo => 0x0120,
307            ParameterId::SlotDescription => 0x0121,
308            ParameterId::DefaultSlotValue => 0x0122,
309            ParameterId::SensorDefinition => 0x0200,
310            ParameterId::SensorValue => 0x0201,
311            ParameterId::RecordSensors => 0x0202,
312            ParameterId::DeviceHours => 0x0400,
313            ParameterId::LampHours => 0x0401,
314            ParameterId::LampStrikes => 0x0402,
315            ParameterId::LampState => 0x0403,
316            ParameterId::LampOnMode => 0x0404,
317            ParameterId::DevicePowerCycles => 0x0405,
318            ParameterId::DisplayInvert => 0x0500,
319            ParameterId::DisplayLevel => 0x0501,
320            ParameterId::PanInvert => 0x0600,
321            ParameterId::TiltInvert => 0x0601,
322            ParameterId::PanTiltSwap => 0x0602,
323            ParameterId::RealTimeClock => 0x0603,
324            ParameterId::IdentifyDevice => 0x1000,
325            ParameterId::ResetDevice => 0x1001,
326            ParameterId::PowerState => 0x1010,
327            ParameterId::PerformSelfTest => 0x1020,
328            ParameterId::SelfTestDescription => 0x1021,
329            ParameterId::CapturePreset => 0x1030,
330            ParameterId::PresetPlayback => 0x1031,
331            // E1.37-1
332            ParameterId::DmxBlockAddress => 0x0140,
333            ParameterId::DmxFailMode => 0x0141,
334            ParameterId::DmxStartupMode => 0x0142,
335            ParameterId::DimmerInfo => 0x0340,
336            ParameterId::MinimumLevel => 0x0341,
337            ParameterId::MaximumLevel => 0x0342,
338            ParameterId::Curve => 0x0343,
339            ParameterId::CurveDescription => 0x0344,
340            ParameterId::OutputResponseTime => 0x0345,
341            ParameterId::OutputResponseTimeDescription => 0x0346,
342            ParameterId::ModulationFrequency => 0x0347,
343            ParameterId::ModulationFrequencyDescription => 0x0348,
344            ParameterId::BurnIn => 0x0440,
345            ParameterId::LockPin => 0x0640,
346            ParameterId::LockState => 0x0641,
347            ParameterId::LockStateDescription => 0x0642,
348            ParameterId::IdentifyMode => 0x1040,
349            ParameterId::PresetInfo => 0x1041,
350            ParameterId::PresetStatus => 0x1042,
351            ParameterId::PresetMergeMode => 0x1043,
352            ParameterId::PowerOnSelfTest => 0x1044,
353            // E1.37-2
354            ParameterId::ListInterfaces => 0x0700,
355            ParameterId::InterfaceLabel => 0x0701,
356            ParameterId::InterfaceHardwareAddressType1 => 0x0702,
357            ParameterId::IpV4DhcpMode => 0x0703,
358            ParameterId::IpV4ZeroConfMode => 0x0704,
359            ParameterId::IpV4CurrentAddress => 0x0705,
360            ParameterId::IpV4StaticAddress => 0x0706,
361            ParameterId::InterfaceRenewDhcp => 0x0707,
362            ParameterId::InterfaceReleaseDhcp => 0x0708,
363            ParameterId::InterfaceApplyConfiguration => 0x0709,
364            ParameterId::IpV4DefaultRoute => 0x070a,
365            ParameterId::DnsIpV4NameServer => 0x070b,
366            ParameterId::DnsHostName => 0x070c,
367            ParameterId::DnsDomainName => 0x070d,
368            // E1.37-7
369            ParameterId::EndpointList => 0x0900,
370            ParameterId::EndpointListChange => 0x0901,
371            ParameterId::IdentifyEndpoint => 0x0902,
372            ParameterId::EndpointToUniverse => 0x0903,
373            ParameterId::EndpointMode => 0x0904,
374            ParameterId::EndpointLabel => 0x0905,
375            ParameterId::RdmTrafficEnable => 0x0906,
376            ParameterId::DiscoveryState => 0x0907,
377            ParameterId::BackgroundDiscovery => 0x0908,
378            ParameterId::EndpointTiming => 0x0909,
379            ParameterId::EndpointTimingDescription => 0x090a,
380            ParameterId::EndpointResponders => 0x090b,
381            ParameterId::EndpointResponderListChange => 0x090c,
382            ParameterId::BindingControlFields => 0x090d,
383            ParameterId::BackgroundQueuedStatusPolicy => 0x090e,
384            ParameterId::BackgroundQueuedStatusPolicyDescription => 0x090f,
385            // E1.33
386            ParameterId::ComponentScope => 0x0800,
387            ParameterId::SearchDomain => 0x0801,
388            ParameterId::TcpCommsStatus => 0x0802,
389            ParameterId::BrokerStatus => 0x0803,
390            ParameterId::ManufacturerSpecific(pid) => pid,
391            ParameterId::Unsupported(pid) => pid,
392        }
393    }
394}
395
396#[derive(Copy, Clone, Debug, PartialEq, Eq)]
397pub struct ProtocolVersion {
398    major: u8,
399    minor: u8,
400}
401
402impl ProtocolVersion {
403    pub fn new(major: u8, minor: u8) -> Self {
404        Self { major, minor }
405    }
406}
407
408impl From<ProtocolVersion> for u16 {
409    fn from(value: ProtocolVersion) -> Self {
410        u16::from_be_bytes([value.major, value.minor])
411    }
412}
413
414impl fmt::Display for ProtocolVersion {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        write!(f, "{}.{}", self.major, self.minor)
417    }
418}
419
420#[derive(Copy, Clone, Debug, PartialEq)]
421pub enum ProductDetail {
422    NotDeclared,
423    Arc,
424    MetalHalide,
425    Incandescent,
426    Led,
427    Fluorescent,
428    ColdCathode,
429    ElectroLuminescent,
430    Laser,
431    FlashTube,
432    ColorScroller,
433    ColorWheel,
434    ColorChange,
435    IrisDouser,
436    DimmingShutter,
437    ProfileShutter,
438    BarnDoorShutter,
439    EffectsDisc,
440    GoboRotator,
441    Video,
442    Slide,
443    Film,
444    OilWheel,
445    LcdGate,
446    FoggerGlycol,
447    FoggerMineralOil,
448    FoggerWater,
449    CO2,
450    LN2,
451    Bubble,
452    FlamePropane,
453    FlameOther,
454    OlefactoryStimulator,
455    Snow,
456    WaterJet,
457    Wind,
458    Confetti,
459    Hazard,
460    PhaseControl,
461    ReversePhaseControl,
462    Sine,
463    Pwm,
464    Dc,
465    HfBallast,
466    HfHvNeonBallast,
467    HfHvEl,
468    MhrBallast,
469    BitangleModulation,
470    FrequencyModulation,
471    HighFrequency12V,
472    RelayMechanical,
473    RelayElectronic,
474    SwitchElectronic,
475    Contactor,
476    MirrorBallRotator,
477    OtherRotator,
478    KabukiDrop,
479    Curtain,
480    LineSet,
481    MotorControl,
482    DamperControl,
483    Splitter,
484    EthernetNode,
485    Merge,
486    DataPatch,
487    WirelessLink,
488    ProtocolConvertor,
489    AnalogDemultiplex,
490    AnalogMultiplex,
491    SwitchPanel,
492    Router,
493    Fader,
494    Mixer,
495    ChangeOverManual,
496    ChangeOverAuto,
497    Test,
498    GfiRcd,
499    Battery,
500    ControllableBreaker,
501    Other,
502    ManufacturerSpecific(u16),
503    Unknown(u16),
504}
505
506impl From<u16> for ProductDetail {
507    fn from(value: u16) -> Self {
508        match value {
509            0x0000 => Self::NotDeclared,
510            0x0001 => Self::Arc,
511            0x0002 => Self::MetalHalide,
512            0x0003 => Self::Incandescent,
513            0x0004 => Self::Led,
514            0x0005 => Self::Fluorescent,
515            0x0006 => Self::ColdCathode,
516            0x0007 => Self::ElectroLuminescent,
517            0x0008 => Self::Laser,
518            0x0009 => Self::FlashTube,
519            0x0100 => Self::ColorScroller,
520            0x0101 => Self::ColorWheel,
521            0x0102 => Self::ColorChange,
522            0x0103 => Self::IrisDouser,
523            0x0104 => Self::DimmingShutter,
524            0x0105 => Self::ProfileShutter,
525            0x0106 => Self::BarnDoorShutter,
526            0x0107 => Self::EffectsDisc,
527            0x0108 => Self::GoboRotator,
528            0x0200 => Self::Video,
529            0x0201 => Self::Slide,
530            0x0202 => Self::Film,
531            0x0203 => Self::OilWheel,
532            0x0204 => Self::LcdGate,
533            0x0300 => Self::FoggerGlycol,
534            0x0301 => Self::FoggerMineralOil,
535            0x0302 => Self::FoggerWater,
536            0x0303 => Self::CO2,
537            0x0304 => Self::LN2,
538            0x0305 => Self::Bubble,
539            0x0306 => Self::FlamePropane,
540            0x0307 => Self::FlameOther,
541            0x0308 => Self::OlefactoryStimulator,
542            0x0309 => Self::Snow,
543            0x030a => Self::WaterJet,
544            0x030b => Self::Wind,
545            0x030c => Self::Confetti,
546            0x030d => Self::Hazard,
547            0x0400 => Self::PhaseControl,
548            0x0401 => Self::ReversePhaseControl,
549            0x0402 => Self::Sine,
550            0x0403 => Self::Pwm,
551            0x0404 => Self::Dc,
552            0x0405 => Self::HfBallast,
553            0x0406 => Self::HfHvNeonBallast,
554            0x0407 => Self::HfHvEl,
555            0x0408 => Self::MhrBallast,
556            0x0409 => Self::BitangleModulation,
557            0x040a => Self::FrequencyModulation,
558            0x040b => Self::HighFrequency12V,
559            0x040c => Self::RelayMechanical,
560            0x040d => Self::RelayElectronic,
561            0x040e => Self::SwitchElectronic,
562            0x040f => Self::Contactor,
563            0x0500 => Self::MirrorBallRotator,
564            0x0501 => Self::OtherRotator,
565            0x0502 => Self::KabukiDrop,
566            0x0503 => Self::Curtain,
567            0x0504 => Self::LineSet,
568            0x0505 => Self::MotorControl,
569            0x0506 => Self::DamperControl,
570            0x0600 => Self::Splitter,
571            0x0601 => Self::EthernetNode,
572            0x0602 => Self::Merge,
573            0x0603 => Self::DataPatch,
574            0x0604 => Self::WirelessLink,
575            0x0701 => Self::ProtocolConvertor,
576            0x0702 => Self::AnalogDemultiplex,
577            0x0703 => Self::AnalogMultiplex,
578            0x0704 => Self::SwitchPanel,
579            0x0800 => Self::Router,
580            0x0801 => Self::Fader,
581            0x0802 => Self::Mixer,
582            0x0900 => Self::ChangeOverManual,
583            0x0901 => Self::ChangeOverAuto,
584            0x0902 => Self::Test,
585            0x0a00 => Self::GfiRcd,
586            0x0a01 => Self::Battery,
587            0x0a02 => Self::ControllableBreaker,
588            0x7fff => Self::Other,
589            value if (0x8000..=0xdfff).contains(&value) => Self::ManufacturerSpecific(value),
590            value => Self::Unknown(value),
591        }
592    }
593}
594
595impl From<ProductDetail> for u16 {
596    fn from(value: ProductDetail) -> Self {
597        match value {
598            ProductDetail::NotDeclared => 0x0000,
599            ProductDetail::Arc => 0x0001,
600            ProductDetail::MetalHalide => 0x0002,
601            ProductDetail::Incandescent => 0x0003,
602            ProductDetail::Led => 0x0004,
603            ProductDetail::Fluorescent => 0x0005,
604            ProductDetail::ColdCathode => 0x0006,
605            ProductDetail::ElectroLuminescent => 0x0007,
606            ProductDetail::Laser => 0x0008,
607            ProductDetail::FlashTube => 0x0009,
608            ProductDetail::ColorScroller => 0x0100,
609            ProductDetail::ColorWheel => 0x0101,
610            ProductDetail::ColorChange => 0x0102,
611            ProductDetail::IrisDouser => 0x0103,
612            ProductDetail::DimmingShutter => 0x0104,
613            ProductDetail::ProfileShutter => 0x0105,
614            ProductDetail::BarnDoorShutter => 0x0106,
615            ProductDetail::EffectsDisc => 0x0107,
616            ProductDetail::GoboRotator => 0x0108,
617            ProductDetail::Video => 0x0200,
618            ProductDetail::Slide => 0x0201,
619            ProductDetail::Film => 0x0202,
620            ProductDetail::OilWheel => 0x0203,
621            ProductDetail::LcdGate => 0x0204,
622            ProductDetail::FoggerGlycol => 0x0300,
623            ProductDetail::FoggerMineralOil => 0x0301,
624            ProductDetail::FoggerWater => 0x0302,
625            ProductDetail::CO2 => 0x0303,
626            ProductDetail::LN2 => 0x0304,
627            ProductDetail::Bubble => 0x0305,
628            ProductDetail::FlamePropane => 0x0306,
629            ProductDetail::FlameOther => 0x0307,
630            ProductDetail::OlefactoryStimulator => 0x0308,
631            ProductDetail::Snow => 0x0309,
632            ProductDetail::WaterJet => 0x030a,
633            ProductDetail::Wind => 0x030b,
634            ProductDetail::Confetti => 0x030c,
635            ProductDetail::Hazard => 0x030d,
636            ProductDetail::PhaseControl => 0x0400,
637            ProductDetail::ReversePhaseControl => 0x0401,
638            ProductDetail::Sine => 0x0402,
639            ProductDetail::Pwm => 0x0403,
640            ProductDetail::Dc => 0x0404,
641            ProductDetail::HfBallast => 0x0405,
642            ProductDetail::HfHvNeonBallast => 0x0406,
643            ProductDetail::HfHvEl => 0x0407,
644            ProductDetail::MhrBallast => 0x0408,
645            ProductDetail::BitangleModulation => 0x0409,
646            ProductDetail::FrequencyModulation => 0x040a,
647            ProductDetail::HighFrequency12V => 0x040b,
648            ProductDetail::RelayMechanical => 0x040c,
649            ProductDetail::RelayElectronic => 0x040d,
650            ProductDetail::SwitchElectronic => 0x040e,
651            ProductDetail::Contactor => 0x040f,
652            ProductDetail::MirrorBallRotator => 0x0500,
653            ProductDetail::OtherRotator => 0x0501,
654            ProductDetail::KabukiDrop => 0x0502,
655            ProductDetail::Curtain => 0x0503,
656            ProductDetail::LineSet => 0x0504,
657            ProductDetail::MotorControl => 0x0505,
658            ProductDetail::DamperControl => 0x0506,
659            ProductDetail::Splitter => 0x0600,
660            ProductDetail::EthernetNode => 0x0601,
661            ProductDetail::Merge => 0x0602,
662            ProductDetail::DataPatch => 0x0603,
663            ProductDetail::WirelessLink => 0x0604,
664            ProductDetail::ProtocolConvertor => 0x0701,
665            ProductDetail::AnalogDemultiplex => 0x0702,
666            ProductDetail::AnalogMultiplex => 0x0703,
667            ProductDetail::SwitchPanel => 0x0704,
668            ProductDetail::Router => 0x0800,
669            ProductDetail::Fader => 0x0801,
670            ProductDetail::Mixer => 0x0802,
671            ProductDetail::ChangeOverManual => 0x0900,
672            ProductDetail::ChangeOverAuto => 0x0901,
673            ProductDetail::Test => 0x0902,
674            ProductDetail::GfiRcd => 0x0a00,
675            ProductDetail::Battery => 0x0a01,
676            ProductDetail::ControllableBreaker => 0x0a02,
677            ProductDetail::Other => 0x7fff,
678            ProductDetail::ManufacturerSpecific(value) => value,
679            ProductDetail::Unknown(value) => value,
680        }
681    }
682}
683
684#[derive(Copy, Clone, Debug, PartialEq)]
685pub enum ImplementedCommandClass {
686    Get = 0x01,
687    Set = 0x02,
688    GetSet = 0x03,
689}
690
691impl TryFrom<u8> for ImplementedCommandClass {
692    type Error = RdmError;
693
694    fn try_from(value: u8) -> Result<Self, Self::Error> {
695        match value {
696            0x01 => Ok(Self::Get),
697            0x02 => Ok(Self::Set),
698            0x03 => Ok(Self::GetSet),
699            _ => Err(RdmError::InvalidCommandClassImplementation(value)),
700        }
701    }
702}
703
704#[derive(Copy, Clone, Debug, PartialEq)]
705pub enum ParameterDataType {
706    NotDefined,
707    BitField,
708    Ascii,
709    UnsignedByte,
710    SignedByte,
711    UnsignedWord,
712    SignedWord,
713    UnsignedDWord,
714    SignedDWord,
715    ManufacturerSpecific(u8),
716}
717
718impl TryFrom<u8> for ParameterDataType {
719    type Error = RdmError;
720
721    fn try_from(value: u8) -> Result<Self, RdmError> {
722        match value {
723            0x00 => Ok(Self::NotDefined),
724            0x01 => Ok(Self::BitField),
725            0x02 => Ok(Self::Ascii),
726            0x03 => Ok(Self::UnsignedByte),
727            0x04 => Ok(Self::SignedByte),
728            0x05 => Ok(Self::UnsignedWord),
729            0x06 => Ok(Self::SignedWord),
730            0x07 => Ok(Self::UnsignedDWord),
731            0x08 => Ok(Self::SignedDWord),
732            n if (0x80..=0xdf).contains(&n) => Ok(Self::ManufacturerSpecific(n)),
733            _ => Err(RdmError::InvalidParameterDataType(value)),
734        }
735    }
736}
737
738impl From<ParameterDataType> for u8 {
739    fn from(value: ParameterDataType) -> Self {
740        match value {
741            ParameterDataType::NotDefined => 0x00,
742            ParameterDataType::BitField => 0x01,
743            ParameterDataType::Ascii => 0x02,
744            ParameterDataType::UnsignedByte => 0x03,
745            ParameterDataType::SignedByte => 0x04,
746            ParameterDataType::UnsignedWord => 0x05,
747            ParameterDataType::SignedWord => 0x06,
748            ParameterDataType::UnsignedDWord => 0x07,
749            ParameterDataType::SignedDWord => 0x08,
750            ParameterDataType::ManufacturerSpecific(n) => n,
751        }
752    }
753}
754
755pub enum ConvertedParameterValue {
756    BitField(u8),
757    Ascii(
758        #[cfg(feature = "alloc")] String,
759        #[cfg(not(feature = "alloc"))] String<4>,
760    ),
761    UnsignedByte(u8),
762    SignedByte(i8),
763    UnsignedWord(u16),
764    SignedWord(i16),
765    UnsignedDWord(u32),
766    SignedDWord(i32),
767    Raw([u8; 4]),
768}
769
770#[derive(Clone, Debug, PartialEq)]
771pub struct ParameterDescription {
772    pub parameter_id: u16,
773    pub parameter_data_length: u8,
774    pub data_type: ParameterDataType,
775    pub command_class: ImplementedCommandClass,
776    pub unit_type: SensorUnit,
777    pub prefix: SensorUnitPrefix,
778    pub raw_minimum_valid_value: [u8; 4],
779    pub raw_maximum_valid_value: [u8; 4],
780    pub raw_default_value: [u8; 4],
781    #[cfg(feature = "alloc")]
782    pub description: String,
783    #[cfg(not(feature = "alloc"))]
784    pub description: String<32>,
785}
786
787impl ParameterDescription {
788    fn convert_parameter_value(
789        parameter_data_type: ParameterDataType,
790        value: [u8; 4],
791    ) -> Result<ConvertedParameterValue, RdmError> {
792        match parameter_data_type {
793            ParameterDataType::BitField => Ok(ConvertedParameterValue::BitField(value[3])),
794            ParameterDataType::Ascii => {
795                Ok(ConvertedParameterValue::Ascii(decode_string_bytes(&value)?))
796            }
797            ParameterDataType::UnsignedByte => Ok(ConvertedParameterValue::UnsignedByte(value[3])),
798            ParameterDataType::SignedByte => {
799                Ok(ConvertedParameterValue::SignedByte(value[3] as i8))
800            }
801            ParameterDataType::UnsignedWord => {
802                Ok(ConvertedParameterValue::UnsignedWord(u16::from_be_bytes([
803                    value[2], value[3],
804                ])))
805            }
806            ParameterDataType::SignedWord => {
807                Ok(ConvertedParameterValue::SignedWord(i16::from_be_bytes([
808                    value[2], value[3],
809                ])))
810            }
811            ParameterDataType::UnsignedDWord => Ok(ConvertedParameterValue::UnsignedDWord(
812                u32::from_be_bytes(value),
813            )),
814            ParameterDataType::SignedDWord => Ok(ConvertedParameterValue::SignedDWord(
815                i32::from_be_bytes(value),
816            )),
817            ParameterDataType::NotDefined | ParameterDataType::ManufacturerSpecific(..) => {
818                Ok(ConvertedParameterValue::Raw(value))
819            }
820        }
821    }
822
823    pub fn minimum_valid_value(&self) -> Result<ConvertedParameterValue, RdmError> {
824        Self::convert_parameter_value(self.data_type, self.raw_minimum_valid_value)
825    }
826    pub fn maximum_valid_value(&self) -> Result<ConvertedParameterValue, RdmError> {
827        Self::convert_parameter_value(self.data_type, self.raw_maximum_valid_value)
828    }
829    pub fn default_value(&self) -> Result<ConvertedParameterValue, RdmError> {
830        Self::convert_parameter_value(self.data_type, self.raw_default_value)
831    }
832}
833
834#[derive(Copy, Clone, Debug, PartialEq)]
835pub enum StatusType {
836    None = 0x00,
837    GetLastMessage = 0x01,
838    Advisory = 0x02,
839    Warning = 0x03,
840    Error = 0x04,
841    AdvisoryCleared = 0x12,
842    WarningCleared = 0x13,
843    ErrorCleared = 0x14,
844}
845
846impl TryFrom<u8> for StatusType {
847    type Error = RdmError;
848
849    fn try_from(value: u8) -> Result<Self, RdmError> {
850        match value {
851            0x00 => Ok(Self::None),
852            0x01 => Ok(Self::GetLastMessage),
853            0x02 => Ok(Self::Advisory),
854            0x03 => Ok(Self::Warning),
855            0x04 => Ok(Self::Error),
856            0x12 => Ok(Self::AdvisoryCleared),
857            0x13 => Ok(Self::WarningCleared),
858            0x14 => Ok(Self::ErrorCleared),
859            _ => Err(RdmError::InvalidStatusType(value)),
860        }
861    }
862}
863
864// Product Categories - Page 105 RDM Spec
865#[derive(Copy, Clone, Debug, PartialEq)]
866pub enum ProductCategory {
867    NotDeclared,
868    Fixture,
869    FixtureFixed,
870    FixtureMovingYoke,
871    FixtureMovingMirror,
872    FixtureOther,
873    FixtureAccessory,
874    FixtureAccessoryColor,
875    FixtureAccessoryYoke,
876    FixtureAccessoryMirror,
877    FixtureAccessoryEffect,
878    FixtureAccessoryBeam,
879    AccessoryOther,
880    Projector,
881    ProjectorFixed,
882    ProjectorMovingYoke,
883    ProjectorMovingMirror,
884    ProjectorOther,
885    Atmospheric,
886    AtmosphericEffect,
887    AtmosphericPyro,
888    AtmosphericOther,
889    Dimmer,
890    DimmerACIncandescent,
891    DimmerACFlourescent,
892    DimmerACColdCathode,
893    DimmerACNonDimModule,
894    DimmerACLowVoltage,
895    DimmerControllableAC,
896    DimmerDCLevelOutput,
897    DimmerDCPWMOutput,
898    DimmerSpecialisedLED,
899    DimmerOther,
900    Power,
901    PowerControl,
902    PowerSource,
903    PowerOther,
904    Scenic,
905    ScenicDrive,
906    ScenicOther,
907    Data,
908    DataDistribution,
909    DataConversion,
910    DataOther,
911    AV,
912    AVAudio,
913    AVVideo,
914    AVOther,
915    Monitor,
916    MonitorACLinePower,
917    MonitorDCPower,
918    MonitorEnvironmental,
919    MonitorOther,
920    Control,
921    ControlController,
922    ControlBackupDevice,
923    ControlOther,
924    Test,
925    TestEquipment,
926    TestEquipmentOther,
927    Other,
928    ManufacturerSpecific(u16),
929    Unknown(u16),
930}
931
932impl From<u16> for ProductCategory {
933    fn from(value: u16) -> Self {
934        match value {
935            0x0000 => Self::NotDeclared,
936            0x0100 => Self::Fixture,
937            0x0101 => Self::FixtureFixed,
938            0x0102 => Self::FixtureMovingYoke,
939            0x0103 => Self::FixtureMovingMirror,
940            0x01ff => Self::FixtureOther,
941            0x0200 => Self::FixtureAccessory,
942            0x0201 => Self::FixtureAccessoryColor,
943            0x0202 => Self::FixtureAccessoryYoke,
944            0x0203 => Self::FixtureAccessoryMirror,
945            0x0204 => Self::FixtureAccessoryEffect,
946            0x0205 => Self::FixtureAccessoryBeam,
947            0x02ff => Self::AccessoryOther,
948            0x0300 => Self::Projector,
949            0x0301 => Self::ProjectorFixed,
950            0x0302 => Self::ProjectorMovingYoke,
951            0x0303 => Self::ProjectorMovingMirror,
952            0x03ff => Self::ProjectorOther,
953            0x0400 => Self::Atmospheric,
954            0x0401 => Self::AtmosphericEffect,
955            0x0402 => Self::AtmosphericPyro,
956            0x04ff => Self::AtmosphericOther,
957            0x0500 => Self::Dimmer,
958            0x0501 => Self::DimmerACIncandescent,
959            0x0502 => Self::DimmerACFlourescent,
960            0x0503 => Self::DimmerACColdCathode,
961            0x0504 => Self::DimmerACNonDimModule,
962            0x0505 => Self::DimmerACLowVoltage,
963            0x0506 => Self::DimmerControllableAC,
964            0x0507 => Self::DimmerDCLevelOutput,
965            0x0508 => Self::DimmerDCPWMOutput,
966            0x0509 => Self::DimmerSpecialisedLED,
967            0x05ff => Self::DimmerOther,
968            0x0600 => Self::Power,
969            0x0601 => Self::PowerControl,
970            0x0602 => Self::PowerSource,
971            0x06ff => Self::PowerOther,
972            0x0700 => Self::Scenic,
973            0x0701 => Self::ScenicDrive,
974            0x07ff => Self::ScenicOther,
975            0x0800 => Self::Data,
976            0x0801 => Self::DataDistribution,
977            0x0802 => Self::DataConversion,
978            0x08ff => Self::DataOther,
979            0x0900 => Self::AV,
980            0x0901 => Self::AVAudio,
981            0x0902 => Self::AVVideo,
982            0x09ff => Self::AVOther,
983            0x0a00 => Self::Monitor,
984            0x0a01 => Self::MonitorACLinePower,
985            0x0a02 => Self::MonitorDCPower,
986            0x0a03 => Self::MonitorEnvironmental,
987            0x0aff => Self::MonitorOther,
988            0x7000 => Self::Control,
989            0x7001 => Self::ControlController,
990            0x7002 => Self::ControlBackupDevice,
991            0x70ff => Self::ControlOther,
992            0x7100 => Self::Test,
993            0x7101 => Self::TestEquipment,
994            0x71ff => Self::TestEquipmentOther,
995            0x7fff => Self::Other,
996            value if (0x8000..=0xdfff).contains(&value) => Self::ManufacturerSpecific(value),
997            value => Self::Unknown(value),
998        }
999    }
1000}
1001
1002impl From<ProductCategory> for u16 {
1003    fn from(value: ProductCategory) -> Self {
1004        match value {
1005            ProductCategory::NotDeclared => 0x0000,
1006            ProductCategory::Fixture => 0x0100,
1007            ProductCategory::FixtureFixed => 0x0101,
1008            ProductCategory::FixtureMovingYoke => 0x0102,
1009            ProductCategory::FixtureMovingMirror => 0x0103,
1010            ProductCategory::FixtureOther => 0x01ff,
1011            ProductCategory::FixtureAccessory => 0x0200,
1012            ProductCategory::FixtureAccessoryColor => 0x0201,
1013            ProductCategory::FixtureAccessoryYoke => 0x0202,
1014            ProductCategory::FixtureAccessoryMirror => 0x0203,
1015            ProductCategory::FixtureAccessoryEffect => 0x0204,
1016            ProductCategory::FixtureAccessoryBeam => 0x0205,
1017            ProductCategory::AccessoryOther => 0x02ff,
1018            ProductCategory::Projector => 0x0300,
1019            ProductCategory::ProjectorFixed => 0x0301,
1020            ProductCategory::ProjectorMovingYoke => 0x0302,
1021            ProductCategory::ProjectorMovingMirror => 0x0303,
1022            ProductCategory::ProjectorOther => 0x03ff,
1023            ProductCategory::Atmospheric => 0x0400,
1024            ProductCategory::AtmosphericEffect => 0x0401,
1025            ProductCategory::AtmosphericPyro => 0x0402,
1026            ProductCategory::AtmosphericOther => 0x04ff,
1027            ProductCategory::Dimmer => 0x0500,
1028            ProductCategory::DimmerACIncandescent => 0x0501,
1029            ProductCategory::DimmerACFlourescent => 0x0502,
1030            ProductCategory::DimmerACColdCathode => 0x0503,
1031            ProductCategory::DimmerACNonDimModule => 0x0504,
1032            ProductCategory::DimmerACLowVoltage => 0x0505,
1033            ProductCategory::DimmerControllableAC => 0x0506,
1034            ProductCategory::DimmerDCLevelOutput => 0x0507,
1035            ProductCategory::DimmerDCPWMOutput => 0x0508,
1036            ProductCategory::DimmerSpecialisedLED => 0x0509,
1037            ProductCategory::DimmerOther => 0x05ff,
1038            ProductCategory::Power => 0x0600,
1039            ProductCategory::PowerControl => 0x0601,
1040            ProductCategory::PowerSource => 0x0602,
1041            ProductCategory::PowerOther => 0x06ff,
1042            ProductCategory::Scenic => 0x0700,
1043            ProductCategory::ScenicDrive => 0x0701,
1044            ProductCategory::ScenicOther => 0x07ff,
1045            ProductCategory::Data => 0x0800,
1046            ProductCategory::DataDistribution => 0x0801,
1047            ProductCategory::DataConversion => 0x0802,
1048            ProductCategory::DataOther => 0x08ff,
1049            ProductCategory::AV => 0x0900,
1050            ProductCategory::AVAudio => 0x0901,
1051            ProductCategory::AVVideo => 0x0902,
1052            ProductCategory::AVOther => 0x09ff,
1053            ProductCategory::Monitor => 0x0a00,
1054            ProductCategory::MonitorACLinePower => 0x0a01,
1055            ProductCategory::MonitorDCPower => 0x0a02,
1056            ProductCategory::MonitorEnvironmental => 0x0a03,
1057            ProductCategory::MonitorOther => 0x0aff,
1058            ProductCategory::Control => 0x7000,
1059            ProductCategory::ControlController => 0x7001,
1060            ProductCategory::ControlBackupDevice => 0x7002,
1061            ProductCategory::ControlOther => 0x70ff,
1062            ProductCategory::Test => 0x7100,
1063            ProductCategory::TestEquipment => 0x7101,
1064            ProductCategory::TestEquipmentOther => 0x71ff,
1065            ProductCategory::Other => 0x7fff,
1066            ProductCategory::ManufacturerSpecific(value) => value,
1067            ProductCategory::Unknown(value) => value,
1068        }
1069    }
1070}
1071
1072#[derive(Copy, Clone, Debug, PartialEq)]
1073pub enum LampState {
1074    LampOff,
1075    LampOn,
1076    LampStrike,
1077    LampStandby,
1078    LampNotPresent,
1079    LampError,
1080    ManufacturerSpecific(u8),
1081}
1082
1083impl TryFrom<u8> for LampState {
1084    type Error = RdmError;
1085
1086    fn try_from(value: u8) -> Result<Self, Self::Error> {
1087        match value {
1088            0x00 => Ok(Self::LampOff),
1089            0x01 => Ok(Self::LampOn),
1090            0x02 => Ok(Self::LampStrike),
1091            0x03 => Ok(Self::LampStandby),
1092            0x04 => Ok(Self::LampNotPresent),
1093            0x05 => Ok(Self::LampError),
1094            n if (0x80..=0xdf).contains(&n) => Ok(Self::ManufacturerSpecific(n)),
1095            _ => Err(RdmError::InvalidLampState(value)),
1096        }
1097    }
1098}
1099
1100impl From<LampState> for u8 {
1101    fn from(value: LampState) -> u8 {
1102        match value {
1103            LampState::LampOff => 0x00,
1104            LampState::LampOn => 0x01,
1105            LampState::LampStrike => 0x02,
1106            LampState::LampStandby => 0x03,
1107            LampState::LampNotPresent => 0x04,
1108            LampState::LampError => 0x05,
1109            LampState::ManufacturerSpecific(n) => n,
1110        }
1111    }
1112}
1113
1114#[derive(Copy, Clone, Debug, PartialEq)]
1115pub enum LampOnMode {
1116    OffMode,
1117    DmxMode,
1118    OnMode,
1119    AfterCal,
1120    ManufacturerSpecific(u8),
1121}
1122
1123impl TryFrom<u8> for LampOnMode {
1124    type Error = RdmError;
1125
1126    fn try_from(value: u8) -> Result<Self, Self::Error> {
1127        match value {
1128            0x00 => Ok(Self::OffMode),
1129            0x01 => Ok(Self::DmxMode),
1130            0x02 => Ok(Self::OnMode),
1131            0x03 => Ok(Self::AfterCal),
1132            n if (0x80..=0xdf).contains(&n) => Ok(Self::ManufacturerSpecific(n)),
1133            _ => Err(RdmError::InvalidLampOnMode(value)),
1134        }
1135    }
1136}
1137
1138impl From<LampOnMode> for u8 {
1139    fn from(value: LampOnMode) -> u8 {
1140        match value {
1141            LampOnMode::OffMode => 0x00,
1142            LampOnMode::DmxMode => 0x01,
1143            LampOnMode::OnMode => 0x02,
1144            LampOnMode::AfterCal => 0x03,
1145            LampOnMode::ManufacturerSpecific(n) => n,
1146        }
1147    }
1148}
1149
1150#[derive(Copy, Clone, Debug, PartialEq)]
1151pub enum PowerState {
1152    FullOff = 0x00,
1153    Shutdown = 0x01,
1154    Standby = 0x02,
1155    Normal = 0xff,
1156}
1157
1158impl TryFrom<u8> for PowerState {
1159    type Error = RdmError;
1160
1161    fn try_from(value: u8) -> Result<Self, Self::Error> {
1162        match value {
1163            0x00 => Ok(Self::FullOff),
1164            0x01 => Ok(Self::Shutdown),
1165            0x02 => Ok(Self::Standby),
1166            0x03 => Ok(Self::Normal),
1167            _ => Err(RdmError::InvalidPowerState(value)),
1168        }
1169    }
1170}
1171
1172#[derive(Copy, Clone, Debug, PartialEq)]
1173pub enum OnOffStates {
1174    Off = 0x00,
1175    On = 0x01,
1176}
1177
1178impl TryFrom<u8> for OnOffStates {
1179    type Error = RdmError;
1180
1181    fn try_from(value: u8) -> Result<Self, Self::Error> {
1182        match value {
1183            0x00 => Ok(Self::Off),
1184            0x01 => Ok(Self::On),
1185            _ => Err(RdmError::InvalidOnOffStates(value)),
1186        }
1187    }
1188}
1189
1190#[derive(Copy, Clone, Debug, PartialEq)]
1191pub enum DisplayInvertMode {
1192    Off = 0x00,
1193    On = 0x01,
1194    Auto = 0x02,
1195}
1196
1197impl TryFrom<u8> for DisplayInvertMode {
1198    type Error = RdmError;
1199
1200    fn try_from(value: u8) -> Result<Self, Self::Error> {
1201        match value {
1202            0x00 => Ok(Self::Off),
1203            0x01 => Ok(Self::On),
1204            0x02 => Ok(Self::Auto),
1205            _ => Err(RdmError::InvalidDisplayInvertMode(value)),
1206        }
1207    }
1208}
1209
1210#[derive(Copy, Clone, Debug, PartialEq)]
1211pub enum ResetDeviceMode {
1212    Warm = 0x01,
1213    Cold = 0xff,
1214}
1215
1216impl TryFrom<u8> for ResetDeviceMode {
1217    type Error = RdmError;
1218
1219    fn try_from(value: u8) -> Result<Self, Self::Error> {
1220        match value {
1221            0x01 => Ok(Self::Warm),
1222            0xff => Ok(Self::Cold),
1223            _ => Err(RdmError::InvalidResetDeviceMode(value)),
1224        }
1225    }
1226}
1227
1228#[derive(Copy, Clone, Debug, PartialEq)]
1229pub enum SelfTest {
1230    Off,
1231    All,
1232    ManufacturerId(u8),
1233}
1234
1235impl From<u8> for SelfTest {
1236    fn from(value: u8) -> Self {
1237        match value {
1238            0x00 => Self::Off,
1239            0xff => Self::All,
1240            value => Self::ManufacturerId(value),
1241        }
1242    }
1243}
1244
1245impl From<SelfTest> for u8 {
1246    fn from(value: SelfTest) -> u8 {
1247        match value {
1248            SelfTest::Off => 0x00,
1249            SelfTest::All => 0xff,
1250            SelfTest::ManufacturerId(value) => value,
1251        }
1252    }
1253}
1254
1255#[derive(Copy, Clone, Debug, PartialEq)]
1256pub enum PresetPlaybackMode {
1257    Off,
1258    All,
1259    Scene(u16),
1260}
1261
1262impl From<u16> for PresetPlaybackMode {
1263    fn from(value: u16) -> Self {
1264        match value {
1265            0x0000 => Self::Off,
1266            0xffff => Self::All,
1267            value => Self::Scene(value),
1268        }
1269    }
1270}
1271
1272impl From<PresetPlaybackMode> for u16 {
1273    fn from(value: PresetPlaybackMode) -> u16 {
1274        match value {
1275            PresetPlaybackMode::Off => 0x0000,
1276            PresetPlaybackMode::All => 0xffff,
1277            PresetPlaybackMode::Scene(value) => value,
1278        }
1279    }
1280}
1281
1282#[derive(Copy, Clone, Debug, PartialEq)]
1283pub struct FadeTimes {
1284    pub up_fade_time: u16,
1285    pub down_fade_time: u16,
1286    pub wait_time: u16,
1287}
1288
1289#[non_exhaustive]
1290#[derive(Copy, Clone, Debug, PartialEq)]
1291pub enum StatusMessageIdDefinition {
1292    CalibrationFailed = 0x0001,
1293    SensorNotFound = 0x0002,
1294    SensorAlwaysOn = 0x0003,
1295    LampDoused = 0x0011,
1296    LampStrike = 0x0012,
1297    OverTemperature = 0x0021,
1298    UnderTemperature = 0x0022,
1299    SensorOutOfRange = 0x0023,
1300    OverVoltagePhase = 0x0031,
1301    UnderVoltagePhase = 0x0032,
1302    OverCurrent = 0x0033,
1303    UnderCurrent = 0x0034,
1304    Phase = 0x0035,
1305    PhaseError = 0x0036,
1306    Amps = 0x0037,
1307    Volts = 0x0038,
1308    DimSlotOccupied = 0x0041,
1309    BreakerTrip = 0x0042,
1310    Watts = 0x0043,
1311    DimmerFailure = 0x0044,
1312    DimmerPanic = 0x0045,
1313    Ready = 0x0050,
1314    NotReady = 0x0051,
1315    LowFluid = 0x0052,
1316}
1317
1318#[derive(Clone, Debug, PartialEq)]
1319pub struct StatusMessage {
1320    pub sub_device_id: SubDeviceId,
1321    pub status_type: StatusType,
1322    pub status_message_id: u16,
1323    pub data_value1: u16,
1324    pub data_value2: u16,
1325    #[cfg(feature = "alloc")]
1326    pub description: Option<String>,
1327    #[cfg(not(feature = "alloc"))]
1328    pub description: Option<String<32>>,
1329}
1330
1331impl StatusMessage {
1332    pub fn new(
1333        sub_device_id: SubDeviceId,
1334        status_type: StatusType,
1335        status_message_id: u16,
1336        data_value1: u16,
1337        data_value2: u16,
1338    ) -> Self {
1339        let description = if status_message_id < 0x8000 {
1340            match status_message_id {
1341                0x0001 => Some(
1342                    #[cfg(feature = "alloc")]
1343                    format!("{} failed calibration", SlotIdDefinition::from(data_value1)),
1344                    #[cfg(not(feature = "alloc"))]
1345                    String::<32>::from_str(
1346                        format_args!("{} failed calibration", SlotIdDefinition::from(data_value1))
1347                            .as_str()
1348                            .unwrap(),
1349                    )
1350                    .unwrap(),
1351                ),
1352                0x0002 => Some(
1353                    #[cfg(feature = "alloc")]
1354                    format!("{} sensor not found", SlotIdDefinition::from(data_value1)),
1355                    #[cfg(not(feature = "alloc"))]
1356                    String::<32>::from_str(
1357                        format_args!("{} sensor not found", SlotIdDefinition::from(data_value1))
1358                            .as_str()
1359                            .unwrap(),
1360                    )
1361                    .unwrap(),
1362                ),
1363                0x0003 => Some(
1364                    #[cfg(feature = "alloc")]
1365                    format!("{} sensor always on", SlotIdDefinition::from(data_value1)),
1366                    #[cfg(not(feature = "alloc"))]
1367                    String::<32>::from_str(
1368                        format_args!("{} sensor always on", SlotIdDefinition::from(data_value1))
1369                            .as_str()
1370                            .unwrap(),
1371                    )
1372                    .unwrap(),
1373                ),
1374                0x0011 => Some(
1375                    #[cfg(feature = "alloc")]
1376                    "Lamp Doused".to_string(),
1377                    #[cfg(not(feature = "alloc"))]
1378                    String::<32>::from_str("Lamp Doused").unwrap(),
1379                ),
1380                0x0012 => Some(
1381                    #[cfg(feature = "alloc")]
1382                    "Lamp Strike".to_string(),
1383                    #[cfg(not(feature = "alloc"))]
1384                    String::<32>::from_str("Lamp Strike").unwrap(),
1385                ),
1386                0x0021 => Some(
1387                    #[cfg(feature = "alloc")]
1388                    format!(
1389                        "Sensor {} over temp at {} degrees C",
1390                        data_value1, data_value2
1391                    ),
1392                    #[cfg(not(feature = "alloc"))]
1393                    String::<32>::from_str(
1394                        format_args!(
1395                            "Sensor {} over temp at {} degrees C",
1396                            data_value1, data_value2
1397                        )
1398                        .as_str()
1399                        .unwrap(),
1400                    )
1401                    .unwrap(),
1402                ),
1403                0x0022 => Some(
1404                    #[cfg(feature = "alloc")]
1405                    format!(
1406                        "Sensor {} under temp at {} degrees C",
1407                        data_value1, data_value2
1408                    ),
1409                    #[cfg(not(feature = "alloc"))]
1410                    String::<32>::from_str(
1411                        format_args!(
1412                            "Sensor {} under temp at {} degrees C",
1413                            data_value1, data_value2
1414                        )
1415                        .as_str()
1416                        .unwrap(),
1417                    )
1418                    .unwrap(),
1419                ),
1420                0x0023 => Some(
1421                    #[cfg(feature = "alloc")]
1422                    format!("Sensor {} out of range", data_value1),
1423                    #[cfg(not(feature = "alloc"))]
1424                    String::<32>::from_str(
1425                        format_args!("Sensor {} out of range", data_value1)
1426                            .as_str()
1427                            .unwrap(),
1428                    )
1429                    .unwrap(),
1430                ),
1431                0x0031 => Some(
1432                    #[cfg(feature = "alloc")]
1433                    format!("Phase {} over voltage at {} V", data_value1, data_value2),
1434                    #[cfg(not(feature = "alloc"))]
1435                    String::<32>::from_str(
1436                        format_args!("Phase {} over voltage at {} V", data_value1, data_value2)
1437                            .as_str()
1438                            .unwrap(),
1439                    )
1440                    .unwrap(),
1441                ),
1442                0x0032 => Some(
1443                    #[cfg(feature = "alloc")]
1444                    format!("Phase {} under voltage at {} V", data_value1, data_value2),
1445                    #[cfg(not(feature = "alloc"))]
1446                    String::<32>::from_str(
1447                        format_args!("Phase {} under voltage at {} V", data_value1, data_value2)
1448                            .as_str()
1449                            .unwrap(),
1450                    )
1451                    .unwrap(),
1452                ),
1453                0x0033 => Some(
1454                    #[cfg(feature = "alloc")]
1455                    format!("Phase {} over current at {} A", data_value1, data_value2),
1456                    #[cfg(not(feature = "alloc"))]
1457                    String::<32>::from_str(
1458                        format_args!("Phase {} over current at {} A", data_value1, data_value2)
1459                            .as_str()
1460                            .unwrap(),
1461                    )
1462                    .unwrap(),
1463                ),
1464                0x0034 => Some(
1465                    #[cfg(feature = "alloc")]
1466                    format!("Phase {} under current at {} A", data_value1, data_value2),
1467                    #[cfg(not(feature = "alloc"))]
1468                    String::<32>::from_str(
1469                        format_args!("Phase {} under current at {} A", data_value1, data_value2)
1470                            .as_str()
1471                            .unwrap(),
1472                    )
1473                    .unwrap(),
1474                ),
1475                0x0035 => Some(
1476                    #[cfg(feature = "alloc")]
1477                    format!("Phase {} is at {} degrees", data_value1, data_value2),
1478                    #[cfg(not(feature = "alloc"))]
1479                    String::<32>::from_str(
1480                        format_args!("Phase {} is at {} degrees", data_value1, data_value2)
1481                            .as_str()
1482                            .unwrap(),
1483                    )
1484                    .unwrap(),
1485                ),
1486                0x0036 => Some(
1487                    #[cfg(feature = "alloc")]
1488                    format!("Phase {} Error", data_value1),
1489                    #[cfg(not(feature = "alloc"))]
1490                    String::<32>::from_str(
1491                        format_args!("Phase {} Error", data_value1)
1492                            .as_str()
1493                            .unwrap(),
1494                    )
1495                    .unwrap(),
1496                ),
1497                0x0037 => Some(
1498                    #[cfg(feature = "alloc")]
1499                    format!("{} Amps", data_value1),
1500                    #[cfg(not(feature = "alloc"))]
1501                    String::<32>::from_str(format_args!("{} Amps", data_value1).as_str().unwrap())
1502                        .unwrap(),
1503                ),
1504                0x0038 => Some(
1505                    #[cfg(feature = "alloc")]
1506                    format!("{} Volts", data_value1),
1507                    #[cfg(not(feature = "alloc"))]
1508                    String::<32>::from_str(format_args!("{} Volts", data_value1).as_str().unwrap())
1509                        .unwrap(),
1510                ),
1511                0x0041 => Some(
1512                    #[cfg(feature = "alloc")]
1513                    "No Dimmer".to_string(),
1514                    #[cfg(not(feature = "alloc"))]
1515                    String::<32>::from_str("No Dimmer").unwrap(),
1516                ),
1517                0x0042 => Some(
1518                    #[cfg(feature = "alloc")]
1519                    "Tripped Breaker".to_string(),
1520                    #[cfg(not(feature = "alloc"))]
1521                    String::<32>::from_str("Tripped Breaker").unwrap(),
1522                ),
1523                0x0043 => Some(
1524                    #[cfg(feature = "alloc")]
1525                    format!("{} Watts", data_value1),
1526                    #[cfg(not(feature = "alloc"))]
1527                    String::<32>::from_str(format_args!("{} Watts", data_value1).as_str().unwrap())
1528                        .unwrap(),
1529                ),
1530                0x0044 => Some(
1531                    #[cfg(feature = "alloc")]
1532                    "Dimmer Failure".to_string(),
1533                    #[cfg(not(feature = "alloc"))]
1534                    String::<32>::from_str("Dimmer Failure").unwrap(),
1535                ),
1536                0x0045 => Some(
1537                    #[cfg(feature = "alloc")]
1538                    "Panic Mode".to_string(),
1539                    #[cfg(not(feature = "alloc"))]
1540                    String::<32>::from_str("Panic Mode").unwrap(),
1541                ),
1542                0x0050 => Some(
1543                    #[cfg(feature = "alloc")]
1544                    format!("{} ready", SlotIdDefinition::from(data_value1)),
1545                    #[cfg(not(feature = "alloc"))]
1546                    String::<32>::from_str(
1547                        format_args!("{} ready", SlotIdDefinition::from(data_value1))
1548                            .as_str()
1549                            .unwrap(),
1550                    )
1551                    .unwrap(),
1552                ),
1553                0x0051 => Some(
1554                    #[cfg(feature = "alloc")]
1555                    format!("{} not ready", SlotIdDefinition::from(data_value1)),
1556                    #[cfg(not(feature = "alloc"))]
1557                    String::<32>::from_str(
1558                        format_args!("{} not ready", SlotIdDefinition::from(data_value1))
1559                            .as_str()
1560                            .unwrap(),
1561                    )
1562                    .unwrap(),
1563                ),
1564                0x0052 => Some(
1565                    #[cfg(feature = "alloc")]
1566                    format!("{} low fluid", SlotIdDefinition::from(data_value1)),
1567                    #[cfg(not(feature = "alloc"))]
1568                    String::<32>::from_str(
1569                        format_args!("{} low fluid", SlotIdDefinition::from(data_value1))
1570                            .as_str()
1571                            .unwrap(),
1572                    )
1573                    .unwrap(),
1574                ),
1575                _ => None,
1576            }
1577        } else {
1578            None
1579        };
1580
1581        StatusMessage {
1582            sub_device_id,
1583            status_type,
1584            status_message_id,
1585            data_value1,
1586            data_value2,
1587            description,
1588        }
1589    }
1590}
1591
1592#[derive(Copy, Clone, Debug, PartialEq)]
1593#[non_exhaustive]
1594pub enum SlotType {
1595    Primary,
1596    SecondaryFine,
1597    SecondaryTiming,
1598    SecondarySpeed,
1599    SecondaryControl,
1600    SecondaryIndex,
1601    SecondaryRotation,
1602    SecondaryIndexRotate,
1603    SecondaryUndefined,
1604    Unknown(u8),
1605}
1606
1607impl From<u8> for SlotType {
1608    fn from(value: u8) -> Self {
1609        match value {
1610            0x00 => Self::Primary,
1611            0x01 => Self::SecondaryFine,
1612            0x02 => Self::SecondaryTiming,
1613            0x03 => Self::SecondarySpeed,
1614            0x04 => Self::SecondaryControl,
1615            0x05 => Self::SecondaryIndex,
1616            0x06 => Self::SecondaryRotation,
1617            0x07 => Self::SecondaryIndexRotate,
1618            0xff => Self::SecondaryUndefined,
1619            value => Self::Unknown(value),
1620        }
1621    }
1622}
1623
1624impl From<SlotType> for u8 {
1625    fn from(value: SlotType) -> Self {
1626        match value {
1627            SlotType::Primary => 0x00,
1628            SlotType::SecondaryFine => 0x01,
1629            SlotType::SecondaryTiming => 0x02,
1630            SlotType::SecondarySpeed => 0x03,
1631            SlotType::SecondaryControl => 0x04,
1632            SlotType::SecondaryIndex => 0x05,
1633            SlotType::SecondaryRotation => 0x06,
1634            SlotType::SecondaryIndexRotate => 0x07,
1635            SlotType::SecondaryUndefined => 0xff,
1636            SlotType::Unknown(value) => value,
1637        }
1638    }
1639}
1640
1641#[derive(Copy, Clone, Debug, PartialEq)]
1642pub struct SlotInfo {
1643    pub id: u16,
1644    pub r#type: SlotType,
1645    pub label_id: u16,
1646}
1647
1648impl SlotInfo {
1649    pub fn new(id: u16, r#type: SlotType, label_id: u16) -> Self {
1650        Self {
1651            id,
1652            r#type,
1653            label_id,
1654        }
1655    }
1656}
1657
1658#[non_exhaustive]
1659#[derive(Copy, Clone, Debug, PartialEq)]
1660pub enum SlotIdDefinition {
1661    Intensity,
1662    IntensityMaster,
1663    Pan,
1664    Tilt,
1665    ColorWheel,
1666    ColorSubCyan,
1667    ColorSubYellow,
1668    ColorSubMagenta,
1669    ColorAddRed,
1670    ColorAddGreen,
1671    ColorAddBlue,
1672    ColorCorrection,
1673    ColorScroll,
1674    ColorSemaphore,
1675    StaticGoboWheel,
1676    RotoGoboWheel,
1677    PrismWheel,
1678    EffectsWheel,
1679    BeamSizeIris,
1680    Edge,
1681    Frost,
1682    Strobe,
1683    Zoom,
1684    FramingShutter,
1685    ShutterRotate,
1686    Douser,
1687    BarnDoor,
1688    LampControl,
1689    FixtureControl,
1690    FixtureSpeed,
1691    Macro,
1692    Undefined,
1693    ManufacturerSpecific(u16),
1694    Unknown(u16),
1695}
1696
1697impl From<u16> for SlotIdDefinition {
1698    fn from(value: u16) -> Self {
1699        match value {
1700            0x0001 => Self::Intensity,
1701            0x0002 => Self::IntensityMaster,
1702            0x0101 => Self::Pan,
1703            0x0102 => Self::Tilt,
1704            0x0201 => Self::ColorWheel,
1705            0x0202 => Self::ColorSubCyan,
1706            0x0203 => Self::ColorSubYellow,
1707            0x0204 => Self::ColorSubMagenta,
1708            0x0205 => Self::ColorAddRed,
1709            0x0206 => Self::ColorAddGreen,
1710            0x0207 => Self::ColorAddBlue,
1711            0x0208 => Self::ColorCorrection,
1712            0x0209 => Self::ColorScroll,
1713            0x0210 => Self::ColorSemaphore,
1714            0x0301 => Self::StaticGoboWheel,
1715            0x0302 => Self::RotoGoboWheel,
1716            0x0303 => Self::PrismWheel,
1717            0x0304 => Self::EffectsWheel,
1718            0x0401 => Self::BeamSizeIris,
1719            0x0402 => Self::Edge,
1720            0x0403 => Self::Frost,
1721            0x0404 => Self::Strobe,
1722            0x0405 => Self::Zoom,
1723            0x0406 => Self::FramingShutter,
1724            0x0407 => Self::ShutterRotate,
1725            0x0408 => Self::Douser,
1726            0x0409 => Self::BarnDoor,
1727            0x0501 => Self::LampControl,
1728            0x0502 => Self::FixtureControl,
1729            0x0503 => Self::FixtureSpeed,
1730            0x0504 => Self::Macro,
1731            0xffff => Self::Undefined,
1732            value if (0x8000..=0xffdf).contains(&value) => Self::ManufacturerSpecific(value),
1733            value => Self::Unknown(value),
1734        }
1735    }
1736}
1737
1738impl From<SlotIdDefinition> for u16 {
1739    fn from(value: SlotIdDefinition) -> Self {
1740        match value {
1741            SlotIdDefinition::Intensity => 0x0001,
1742            SlotIdDefinition::IntensityMaster => 0x0002,
1743            SlotIdDefinition::Pan => 0x0101,
1744            SlotIdDefinition::Tilt => 0x0102,
1745            SlotIdDefinition::ColorWheel => 0x0201,
1746            SlotIdDefinition::ColorSubCyan => 0x0202,
1747            SlotIdDefinition::ColorSubYellow => 0x0203,
1748            SlotIdDefinition::ColorSubMagenta => 0x0204,
1749            SlotIdDefinition::ColorAddRed => 0x0205,
1750            SlotIdDefinition::ColorAddGreen => 0x0206,
1751            SlotIdDefinition::ColorAddBlue => 0x0207,
1752            SlotIdDefinition::ColorCorrection => 0x0208,
1753            SlotIdDefinition::ColorScroll => 0x0209,
1754            SlotIdDefinition::ColorSemaphore => 0x0210,
1755            SlotIdDefinition::StaticGoboWheel => 0x0301,
1756            SlotIdDefinition::RotoGoboWheel => 0x0302,
1757            SlotIdDefinition::PrismWheel => 0x0303,
1758            SlotIdDefinition::EffectsWheel => 0x0304,
1759            SlotIdDefinition::BeamSizeIris => 0x0401,
1760            SlotIdDefinition::Edge => 0x0402,
1761            SlotIdDefinition::Frost => 0x0403,
1762            SlotIdDefinition::Strobe => 0x0404,
1763            SlotIdDefinition::Zoom => 0x0405,
1764            SlotIdDefinition::FramingShutter => 0x0406,
1765            SlotIdDefinition::ShutterRotate => 0x0407,
1766            SlotIdDefinition::Douser => 0x0408,
1767            SlotIdDefinition::BarnDoor => 0x0409,
1768            SlotIdDefinition::LampControl => 0x0501,
1769            SlotIdDefinition::FixtureControl => 0x0502,
1770            SlotIdDefinition::FixtureSpeed => 0x0503,
1771            SlotIdDefinition::Macro => 0x0504,
1772            SlotIdDefinition::Undefined => 0xffff,
1773            SlotIdDefinition::ManufacturerSpecific(value) => value,
1774            SlotIdDefinition::Unknown(value) => value,
1775        }
1776    }
1777}
1778
1779impl core::fmt::Display for SlotIdDefinition {
1780    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1781        let definition = match self {
1782            Self::Intensity => "Intensity",
1783            Self::IntensityMaster => "Intensity Master",
1784            Self::Pan => "Pan",
1785            Self::Tilt => "Tilt",
1786            Self::ColorWheel => "Color Wheel",
1787            Self::ColorSubCyan => "Color Sub Cyan",
1788            Self::ColorSubYellow => "Color Sub Yellow",
1789            Self::ColorSubMagenta => "Color Sub Magenta",
1790            Self::ColorAddRed => "Color Add Red",
1791            Self::ColorAddGreen => "Color Add Green",
1792            Self::ColorAddBlue => "Color Add Blue",
1793            Self::ColorCorrection => "Color Correction",
1794            Self::ColorScroll => "Color Scroll",
1795            Self::ColorSemaphore => "Color Semaphore",
1796            Self::StaticGoboWheel => "Static Gobo Wheel",
1797            Self::RotoGoboWheel => "Roto Gobo Wheel",
1798            Self::PrismWheel => "Prism Wheel",
1799            Self::EffectsWheel => "Effects Wheel",
1800            Self::BeamSizeIris => "Beam Size Iris",
1801            Self::Edge => "Edge",
1802            Self::Frost => "Frost",
1803            Self::Strobe => "Strobe",
1804            Self::Zoom => "Zoom",
1805            Self::FramingShutter => "Framing Shutter",
1806            Self::ShutterRotate => "Shutter Rotate",
1807            Self::Douser => "Douser",
1808            Self::BarnDoor => "Barn Door",
1809            Self::LampControl => "Lamp Control",
1810            Self::FixtureControl => "Fixture Control",
1811            Self::FixtureSpeed => "Fixture Speed",
1812            Self::Macro => "Macro",
1813            Self::Undefined => "Undefined",
1814            Self::ManufacturerSpecific(value) => {
1815                return write!(f, "Manufacturer Specific: {}", value)
1816            }
1817            Self::Unknown(value) => return write!(f, "Unknown Slot Id Definition: {}", value),
1818        };
1819
1820        f.write_str(definition)
1821    }
1822}
1823
1824#[derive(Copy, Clone, Debug, PartialEq)]
1825pub struct DefaultSlotValue {
1826    pub id: u16,
1827    pub value: u8,
1828}
1829
1830impl DefaultSlotValue {
1831    pub fn new(id: u16, value: u8) -> Self {
1832        Self { id, value }
1833    }
1834}
1835
1836#[derive(Copy, Clone, Debug, PartialEq)]
1837#[non_exhaustive]
1838pub enum SensorType {
1839    Temperature,
1840    Voltage,
1841    Current,
1842    Frequency,
1843    Resistance,
1844    Power,
1845    Mass,
1846    Length,
1847    Area,
1848    Volume,
1849    Density,
1850    Velocity,
1851    Acceleration,
1852    Force,
1853    Energy,
1854    Pressure,
1855    Time,
1856    Angle,
1857    PositionX,
1858    PositionY,
1859    PositionZ,
1860    AngularVelocity,
1861    LuminousIntensity,
1862    LuminousFlux,
1863    Illuminance,
1864    ChrominanceRed,
1865    ChrominanceGreen,
1866    ChrominanceBlue,
1867    Contacts,
1868    Memory,
1869    Items,
1870    Humidity,
1871    Counter16Bit,
1872    Other,
1873    ManufacturerSpecific(u8),
1874}
1875
1876impl TryFrom<u8> for SensorType {
1877    type Error = RdmError;
1878    fn try_from(value: u8) -> Result<Self, RdmError> {
1879        match value {
1880            0x00 => Ok(Self::Temperature),
1881            0x01 => Ok(Self::Voltage),
1882            0x02 => Ok(Self::Current),
1883            0x03 => Ok(Self::Frequency),
1884            0x04 => Ok(Self::Resistance),
1885            0x05 => Ok(Self::Power),
1886            0x06 => Ok(Self::Mass),
1887            0x07 => Ok(Self::Length),
1888            0x08 => Ok(Self::Area),
1889            0x09 => Ok(Self::Volume),
1890            0x0a => Ok(Self::Density),
1891            0x0b => Ok(Self::Velocity),
1892            0x0c => Ok(Self::Acceleration),
1893            0x0d => Ok(Self::Force),
1894            0x0e => Ok(Self::Energy),
1895            0x0f => Ok(Self::Pressure),
1896            0x10 => Ok(Self::Time),
1897            0x11 => Ok(Self::Angle),
1898            0x12 => Ok(Self::PositionX),
1899            0x13 => Ok(Self::PositionY),
1900            0x14 => Ok(Self::PositionZ),
1901            0x15 => Ok(Self::AngularVelocity),
1902            0x16 => Ok(Self::LuminousIntensity),
1903            0x17 => Ok(Self::LuminousFlux),
1904            0x18 => Ok(Self::Illuminance),
1905            0x19 => Ok(Self::ChrominanceRed),
1906            0x1a => Ok(Self::ChrominanceGreen),
1907            0x1b => Ok(Self::ChrominanceBlue),
1908            0x1c => Ok(Self::Contacts),
1909            0x1d => Ok(Self::Memory),
1910            0x1e => Ok(Self::Items),
1911            0x1f => Ok(Self::Humidity),
1912            0x20 => Ok(Self::Counter16Bit),
1913            0x7f => Ok(Self::Other),
1914            value if (0x80..=0xff).contains(&value) => Ok(Self::ManufacturerSpecific(value)),
1915            _ => Err(RdmError::InvalidSensorType(value)),
1916        }
1917    }
1918}
1919
1920impl From<SensorType> for u8 {
1921    fn from(value: SensorType) -> Self {
1922        match value {
1923            SensorType::Temperature => 0x00,
1924            SensorType::Voltage => 0x01,
1925            SensorType::Current => 0x02,
1926            SensorType::Frequency => 0x03,
1927            SensorType::Resistance => 0x04,
1928            SensorType::Power => 0x05,
1929            SensorType::Mass => 0x06,
1930            SensorType::Length => 0x07,
1931            SensorType::Area => 0x08,
1932            SensorType::Volume => 0x09,
1933            SensorType::Density => 0x0a,
1934            SensorType::Velocity => 0x0b,
1935            SensorType::Acceleration => 0x0c,
1936            SensorType::Force => 0x0d,
1937            SensorType::Energy => 0x0e,
1938            SensorType::Pressure => 0x0f,
1939            SensorType::Time => 0x10,
1940            SensorType::Angle => 0x11,
1941            SensorType::PositionX => 0x12,
1942            SensorType::PositionY => 0x13,
1943            SensorType::PositionZ => 0x14,
1944            SensorType::AngularVelocity => 0x15,
1945            SensorType::LuminousIntensity => 0x16,
1946            SensorType::LuminousFlux => 0x17,
1947            SensorType::Illuminance => 0x18,
1948            SensorType::ChrominanceRed => 0x19,
1949            SensorType::ChrominanceGreen => 0x1a,
1950            SensorType::ChrominanceBlue => 0x1b,
1951            SensorType::Contacts => 0x1c,
1952            SensorType::Memory => 0x1d,
1953            SensorType::Items => 0x1e,
1954            SensorType::Humidity => 0x1f,
1955            SensorType::Counter16Bit => 0x20,
1956            SensorType::Other => 0x7f,
1957            SensorType::ManufacturerSpecific(value) => value,
1958        }
1959    }
1960}
1961
1962#[derive(Copy, Clone, Debug, PartialEq)]
1963#[non_exhaustive]
1964pub enum SensorUnit {
1965    None,
1966    Centigrade,
1967    VoltsDc,
1968    VoltsAcPeak,
1969    VoltsAcRms,
1970    AmpsDc,
1971    AmpsAcPeak,
1972    AmpsAcRms,
1973    Hertz,
1974    Ohm,
1975    Watt,
1976    Kilogram,
1977    Meter,
1978    SquareMeter,
1979    CubicMeter,
1980    KilogramPerCubicMeter,
1981    MeterPerSecond,
1982    MeterPerSecondSquared,
1983    Newton,
1984    Joule,
1985    Pascal,
1986    Second,
1987    Degree,
1988    Steradian,
1989    Candela,
1990    Lumen,
1991    Lux,
1992    Ire,
1993    Byte,
1994    ManufacturerSpecific(u8),
1995}
1996
1997impl TryFrom<u8> for SensorUnit {
1998    type Error = RdmError;
1999
2000    fn try_from(value: u8) -> Result<Self, Self::Error> {
2001        match value {
2002            0x00 => Ok(Self::None),
2003            0x01 => Ok(Self::Centigrade),
2004            0x02 => Ok(Self::VoltsDc),
2005            0x03 => Ok(Self::VoltsAcPeak),
2006            0x04 => Ok(Self::VoltsAcRms),
2007            0x05 => Ok(Self::AmpsDc),
2008            0x06 => Ok(Self::AmpsAcPeak),
2009            0x07 => Ok(Self::AmpsAcRms),
2010            0x08 => Ok(Self::Hertz),
2011            0x09 => Ok(Self::Ohm),
2012            0x0a => Ok(Self::Watt),
2013            0x0b => Ok(Self::Kilogram),
2014            0x0c => Ok(Self::Meter),
2015            0x0d => Ok(Self::SquareMeter),
2016            0x0e => Ok(Self::CubicMeter),
2017            0x0f => Ok(Self::KilogramPerCubicMeter),
2018            0x10 => Ok(Self::MeterPerSecond),
2019            0x11 => Ok(Self::MeterPerSecondSquared),
2020            0x12 => Ok(Self::Newton),
2021            0x13 => Ok(Self::Joule),
2022            0x14 => Ok(Self::Pascal),
2023            0x15 => Ok(Self::Second),
2024            0x16 => Ok(Self::Degree),
2025            0x17 => Ok(Self::Steradian),
2026            0x18 => Ok(Self::Candela),
2027            0x19 => Ok(Self::Lumen),
2028            0x1a => Ok(Self::Lux),
2029            0x1b => Ok(Self::Ire),
2030            0x1c => Ok(Self::Byte),
2031            value if (0x80..=0xff).contains(&value) => Ok(Self::ManufacturerSpecific(value)),
2032            _ => Err(RdmError::InvalidSensorUnit(value)),
2033        }
2034    }
2035}
2036
2037impl From<SensorUnit> for u8 {
2038    fn from(value: SensorUnit) -> Self {
2039        match value {
2040            SensorUnit::None => 0x00,
2041            SensorUnit::Centigrade => 0x01,
2042            SensorUnit::VoltsDc => 0x02,
2043            SensorUnit::VoltsAcPeak => 0x03,
2044            SensorUnit::VoltsAcRms => 0x04,
2045            SensorUnit::AmpsDc => 0x05,
2046            SensorUnit::AmpsAcPeak => 0x06,
2047            SensorUnit::AmpsAcRms => 0x07,
2048            SensorUnit::Hertz => 0x08,
2049            SensorUnit::Ohm => 0x09,
2050            SensorUnit::Watt => 0x0a,
2051            SensorUnit::Kilogram => 0x0b,
2052            SensorUnit::Meter => 0x0c,
2053            SensorUnit::SquareMeter => 0x0d,
2054            SensorUnit::CubicMeter => 0x0e,
2055            SensorUnit::KilogramPerCubicMeter => 0x0f,
2056            SensorUnit::MeterPerSecond => 0x10,
2057            SensorUnit::MeterPerSecondSquared => 0x11,
2058            SensorUnit::Newton => 0x12,
2059            SensorUnit::Joule => 0x13,
2060            SensorUnit::Pascal => 0x14,
2061            SensorUnit::Second => 0x15,
2062            SensorUnit::Degree => 0x16,
2063            SensorUnit::Steradian => 0x17,
2064            SensorUnit::Candela => 0x18,
2065            SensorUnit::Lumen => 0x19,
2066            SensorUnit::Lux => 0x1a,
2067            SensorUnit::Ire => 0x1b,
2068            SensorUnit::Byte => 0x1c,
2069            SensorUnit::ManufacturerSpecific(value) => value,
2070        }
2071    }
2072}
2073
2074#[derive(Copy, Clone, Debug, PartialEq)]
2075pub enum SensorUnitPrefix {
2076    None = 0x00,
2077    Deci = 0x01,
2078    Centi = 0x02,
2079    Milli = 0x03,
2080    Micro = 0x04,
2081    Nano = 0x05,
2082    Pico = 0x06,
2083    Femto = 0x07,
2084    Atto = 0x08,
2085    Zepto = 0x09,
2086    Yocto = 0x0a,
2087    Deca = 0x11,
2088    Hecto = 0x12,
2089    Kilo = 0x13,
2090    Mega = 0x14,
2091    Giga = 0x15,
2092    Terra = 0x16,
2093    Peta = 0x17,
2094    Exa = 0x18,
2095    Zetta = 0x19,
2096    Yotta = 0x1a,
2097}
2098
2099impl TryFrom<u8> for SensorUnitPrefix {
2100    type Error = RdmError;
2101
2102    fn try_from(value: u8) -> Result<Self, Self::Error> {
2103        match value {
2104            0x00 => Ok(Self::None),
2105            0x01 => Ok(Self::Deci),
2106            0x02 => Ok(Self::Centi),
2107            0x03 => Ok(Self::Milli),
2108            0x04 => Ok(Self::Micro),
2109            0x05 => Ok(Self::Nano),
2110            0x06 => Ok(Self::Pico),
2111            0x07 => Ok(Self::Femto),
2112            0x08 => Ok(Self::Atto),
2113            0x09 => Ok(Self::Zepto),
2114            0x0a => Ok(Self::Yocto),
2115            0x11 => Ok(Self::Deca),
2116            0x12 => Ok(Self::Hecto),
2117            0x13 => Ok(Self::Kilo),
2118            0x14 => Ok(Self::Mega),
2119            0x15 => Ok(Self::Giga),
2120            0x16 => Ok(Self::Terra),
2121            0x17 => Ok(Self::Peta),
2122            0x18 => Ok(Self::Exa),
2123            0x19 => Ok(Self::Zetta),
2124            0x1a => Ok(Self::Yotta),
2125            _ => Err(RdmError::InvalidSensorUnitPrefix(value)),
2126        }
2127    }
2128}
2129
2130#[derive(Clone, Debug, PartialEq)]
2131pub struct SensorDefinition {
2132    pub id: u8,
2133    pub kind: SensorType,
2134    pub unit: SensorUnit,
2135    pub prefix: SensorUnitPrefix,
2136    pub range_minimum_value: i16,
2137    pub range_maximum_value: i16,
2138    pub normal_minimum_value: i16,
2139    pub normal_maximum_value: i16,
2140    pub is_lowest_highest_detected_value_supported: bool,
2141    pub is_recorded_value_supported: bool,
2142    #[cfg(feature = "alloc")]
2143    pub description: String,
2144    #[cfg(not(feature = "alloc"))]
2145    pub description: String<32>,
2146}
2147
2148#[derive(Copy, Clone, Debug, PartialEq)]
2149pub struct SensorValue {
2150    pub sensor_id: u8,
2151    pub current_value: i16,
2152    pub lowest_detected_value: i16,
2153    pub highest_detected_value: i16,
2154    pub recorded_value: i16,
2155}
2156
2157impl SensorValue {
2158    pub fn new(
2159        sensor_id: u8,
2160        current_value: i16,
2161        lowest_detected_value: i16,
2162        highest_detected_value: i16,
2163        recorded_value: i16,
2164    ) -> Self {
2165        Self {
2166            sensor_id,
2167            current_value,
2168            lowest_detected_value,
2169            highest_detected_value,
2170            recorded_value,
2171        }
2172    }
2173}
2174
2175#[derive(Copy, Clone, Debug, PartialEq)]
2176pub enum IdentifyMode {
2177    Quiet = 0x00,
2178    Loud = 0xff,
2179}
2180
2181impl TryFrom<u8> for IdentifyMode {
2182    type Error = RdmError;
2183
2184    fn try_from(value: u8) -> Result<Self, Self::Error> {
2185        match value {
2186            0x00 => Ok(Self::Quiet),
2187            0xff => Ok(Self::Loud),
2188            value => Err(RdmError::InvalidIdentifyMode(value)),
2189        }
2190    }
2191}
2192
2193#[derive(Copy, Clone, Debug, PartialEq)]
2194pub enum PresetProgrammed {
2195    NotProgrammed = 0x00,
2196    Programmed = 0x01,
2197    ReadOnly = 0x02,
2198}
2199
2200impl TryFrom<u8> for PresetProgrammed {
2201    type Error = RdmError;
2202
2203    fn try_from(value: u8) -> Result<Self, Self::Error> {
2204        match value {
2205            0x00 => Ok(Self::NotProgrammed),
2206            0x01 => Ok(Self::Programmed),
2207            0x02 => Ok(Self::ReadOnly),
2208            value => Err(RdmError::InvalidPresetProgrammed(value)),
2209        }
2210    }
2211}
2212
2213#[derive(Copy, Clone, Debug, PartialEq)]
2214pub enum MergeMode {
2215    Default = 0x00,
2216    Htp = 0x01,
2217    Ltp = 0x02,
2218    DmxOnly = 0x03,
2219    Other = 0xff,
2220}
2221
2222impl TryFrom<u8> for MergeMode {
2223    type Error = RdmError;
2224
2225    fn try_from(value: u8) -> Result<Self, Self::Error> {
2226        match value {
2227            0x00 => Ok(Self::Default),
2228            0x01 => Ok(Self::Htp),
2229            0x02 => Ok(Self::Ltp),
2230            0x03 => Ok(Self::DmxOnly),
2231            0xff => Ok(Self::Other),
2232            value => Err(RdmError::InvalidMergeMode(value)),
2233        }
2234    }
2235}
2236
2237#[derive(Copy, Clone, Debug, PartialEq)]
2238pub struct PinCode(pub u16);
2239
2240impl TryFrom<u16> for PinCode {
2241    type Error = RdmError;
2242
2243    fn try_from(value: u16) -> Result<Self, Self::Error> {
2244        if value > 9999 {
2245            Err(RdmError::InvalidPinCode(value))
2246        } else {
2247            Ok(Self(value))
2248        }
2249    }
2250}
2251
2252#[derive(Copy, Clone, Debug, PartialEq)]
2253pub enum SupportedTimes {
2254    NotSupported,
2255    Time(u16),
2256}
2257
2258impl From<u16> for SupportedTimes {
2259    fn from(value: u16) -> Self {
2260        match value {
2261            0xffff => Self::NotSupported,
2262            value => Self::Time(value),
2263        }
2264    }
2265}
2266
2267impl From<SupportedTimes> for u16 {
2268    fn from(value: SupportedTimes) -> u16 {
2269        match value {
2270            SupportedTimes::NotSupported => 0xffff,
2271            SupportedTimes::Time(value) => value,
2272        }
2273    }
2274}
2275
2276#[derive(Copy, Clone, Debug, PartialEq)]
2277pub enum TimeMode {
2278    Infinite,
2279    TenthOfSeconds(u16),
2280}
2281
2282impl From<u16> for TimeMode {
2283    fn from(value: u16) -> Self {
2284        match value {
2285            0xffff => Self::Infinite,
2286            value => Self::TenthOfSeconds(value),
2287        }
2288    }
2289}
2290
2291impl From<TimeMode> for u16 {
2292    fn from(value: TimeMode) -> u16 {
2293        match value {
2294            TimeMode::Infinite => 0xffff,
2295            TimeMode::TenthOfSeconds(value) => value,
2296        }
2297    }
2298}
2299
2300#[derive(Copy, Clone, Debug, PartialEq)]
2301pub enum DhcpMode {
2302    Inactive = 0x00,
2303    Active = 0x01,
2304    Unknown = 0x02,
2305}
2306
2307impl TryFrom<u8> for DhcpMode {
2308    type Error = RdmError;
2309
2310    fn try_from(value: u8) -> Result<Self, Self::Error> {
2311        match value {
2312            0x00 => Ok(Self::Inactive),
2313            0x01 => Ok(Self::Active),
2314            0x02 => Ok(Self::Unknown),
2315            value => Err(RdmError::InvalidDhcpMode(value)),
2316        }
2317    }
2318}
2319
2320#[derive(Copy, Clone, Debug, PartialEq)]
2321pub enum Ipv4Address {
2322    Unconfigured,
2323    Configured(Ipv4Addr),
2324}
2325
2326impl From<Ipv4Addr> for Ipv4Address {
2327    fn from(value: Ipv4Addr) -> Self {
2328        Self::Configured(value)
2329    }
2330}
2331
2332impl From<u32> for Ipv4Address {
2333    fn from(value: u32) -> Self {
2334        if value == 0 {
2335            Self::Unconfigured
2336        } else {
2337            Self::Configured(Ipv4Addr::from(value))
2338        }
2339    }
2340}
2341
2342impl From<[u8; 4]> for Ipv4Address {
2343    fn from(value: [u8; 4]) -> Self {
2344        if value == [0, 0, 0, 0] {
2345            Self::Unconfigured
2346        } else {
2347            Self::Configured(Ipv4Addr::from(value))
2348        }
2349    }
2350}
2351
2352impl From<Ipv4Address> for [u8; 4] {
2353    fn from(value: Ipv4Address) -> [u8; 4] {
2354        match value {
2355            Ipv4Address::Unconfigured => [0, 0, 0, 0],
2356            Ipv4Address::Configured(ip) => ip.octets(),
2357        }
2358    }
2359}
2360
2361impl From<Ipv4Address> for u32 {
2362    fn from(value: Ipv4Address) -> u32 {
2363        match value {
2364            Ipv4Address::Unconfigured => 0,
2365            Ipv4Address::Configured(ip) => ip.to_bits(),
2366        }
2367    }
2368}
2369
2370#[derive(Copy, Clone, Debug, PartialEq)]
2371pub enum Ipv6Address {
2372    Unconfigured,
2373    Configured(Ipv6Addr),
2374}
2375
2376impl From<Ipv6Addr> for Ipv6Address {
2377    fn from(value: Ipv6Addr) -> Self {
2378        Self::Configured(value)
2379    }
2380}
2381
2382impl From<u128> for Ipv6Address {
2383    fn from(value: u128) -> Self {
2384        if value == 0 {
2385            Self::Unconfigured
2386        } else {
2387            Self::Configured(Ipv6Addr::from(value))
2388        }
2389    }
2390}
2391
2392impl From<[u8; 16]> for Ipv6Address {
2393    fn from(value: [u8; 16]) -> Self {
2394        if value == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] {
2395            Self::Unconfigured
2396        } else {
2397            Self::Configured(Ipv6Addr::from(value))
2398        }
2399    }
2400}
2401
2402impl From<Ipv6Address> for [u8; 16] {
2403    fn from(value: Ipv6Address) -> [u8; 16] {
2404        match value {
2405            Ipv6Address::Unconfigured => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
2406            Ipv6Address::Configured(ip) => ip.octets(),
2407        }
2408    }
2409}
2410
2411impl From<Ipv6Address> for u128 {
2412    fn from(value: Ipv6Address) -> u128 {
2413        match value {
2414            Ipv6Address::Unconfigured => 0,
2415            Ipv6Address::Configured(ip) => ip.to_bits(),
2416        }
2417    }
2418}
2419
2420#[derive(Copy, Clone, Debug, PartialEq)]
2421pub enum Ipv4Route {
2422    NoDefault,
2423    Configured(Ipv4Addr),
2424}
2425
2426impl From<Ipv4Addr> for Ipv4Route {
2427    fn from(value: Ipv4Addr) -> Self {
2428        Self::Configured(value)
2429    }
2430}
2431
2432impl From<u32> for Ipv4Route {
2433    fn from(value: u32) -> Self {
2434        if value == 0 {
2435            Self::NoDefault
2436        } else {
2437            Self::Configured(Ipv4Addr::from(value))
2438        }
2439    }
2440}
2441
2442impl From<[u8; 4]> for Ipv4Route {
2443    fn from(value: [u8; 4]) -> Self {
2444        if value == [0, 0, 0, 0] {
2445            Self::NoDefault
2446        } else {
2447            Self::Configured(Ipv4Addr::from(value))
2448        }
2449    }
2450}
2451
2452impl From<Ipv4Route> for [u8; 4] {
2453    fn from(value: Ipv4Route) -> [u8; 4] {
2454        match value {
2455            Ipv4Route::NoDefault => [0, 0, 0, 0],
2456            Ipv4Route::Configured(ip) => ip.octets(),
2457        }
2458    }
2459}
2460
2461impl From<Ipv4Route> for u32 {
2462    fn from(value: Ipv4Route) -> u32 {
2463        match value {
2464            Ipv4Route::NoDefault => 0,
2465            Ipv4Route::Configured(ip) => ip.to_bits(),
2466        }
2467    }
2468}
2469
2470// Hardware types are defined by the IANA:
2471// https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
2472#[derive(Copy, Clone, Debug, PartialEq)]
2473pub enum HardwareType {
2474    Reserved(u16),
2475    Ethernet,
2476    ExperimentEthernet,
2477    AmateurRadioAx25,
2478    ProteonPronetTokenRing,
2479    Chaos,
2480    Ieee802Networks,
2481    Arcnet,
2482    Hyperchannel,
2483    Lanstar,
2484    AutonetShortAddress,
2485    LocalTalk,
2486    LocalNet,
2487    UltraLink,
2488    SMDS,
2489    FrameRelay,
2490    ATM,
2491    HDLC,
2492    FibreChannel,
2493    ATMLogical,
2494    SerialLine,
2495    ATMPhysical,
2496    MilStd188220,
2497    Metricom,
2498    IEEE1394,
2499    MAPOS,
2500    Twinaxial,
2501    EUI64,
2502    HIPARP,
2503    IPAndARPOverISO,
2504    ARPSec,
2505    IPsecTunnel,
2506    InfiniBand,
2507    TIA102,
2508    Wiegand,
2509    PureIP,
2510    HwExp1,
2511    Hf1,
2512    UnifiedBus,
2513    HwExp2,
2514    AEthernet,
2515    Unknown(u16),
2516}
2517
2518impl From<u16> for HardwareType {
2519    fn from(value: u16) -> Self {
2520        match value {
2521            val @ (0 | 65535) => Self::Reserved(val),
2522            1 => Self::Ethernet,
2523            2 => Self::ExperimentEthernet,
2524            3 => Self::AmateurRadioAx25,
2525            4 => Self::ProteonPronetTokenRing,
2526            5 => Self::Chaos,
2527            6 => Self::Ieee802Networks,
2528            7 => Self::Arcnet,
2529            8 => Self::Hyperchannel,
2530            9 => Self::Lanstar,
2531            10 => Self::AutonetShortAddress,
2532            11 => Self::LocalTalk,
2533            12 => Self::LocalNet,
2534            13 => Self::UltraLink,
2535            14 => Self::SMDS,
2536            15 => Self::FrameRelay,
2537            16 => Self::ATM,
2538            17 => Self::HDLC,
2539            18 => Self::FibreChannel,
2540            19 => Self::ATMLogical,
2541            20 => Self::SerialLine,
2542            21 => Self::ATMPhysical,
2543            22 => Self::MilStd188220,
2544            23 => Self::Metricom,
2545            24 => Self::IEEE1394,
2546            25 => Self::MAPOS,
2547            26 => Self::Twinaxial,
2548            27 => Self::EUI64,
2549            28 => Self::HIPARP,
2550            29 => Self::IPAndARPOverISO,
2551            30 => Self::ARPSec,
2552            31 => Self::IPsecTunnel,
2553            32 => Self::InfiniBand,
2554            33 => Self::TIA102,
2555            34 => Self::Wiegand,
2556            35 => Self::PureIP,
2557            36 => Self::HwExp1,
2558            37 => Self::Hf1,
2559            38 => Self::UnifiedBus,
2560            256 => Self::HwExp2,
2561            257 => Self::AEthernet,
2562            value => Self::Unknown(value),
2563        }
2564    }
2565}
2566
2567impl From<HardwareType> for u16 {
2568    fn from(value: HardwareType) -> Self {
2569        match value {
2570            HardwareType::Reserved(val) => val,
2571            HardwareType::Ethernet => 1,
2572            HardwareType::ExperimentEthernet => 2,
2573            HardwareType::AmateurRadioAx25 => 3,
2574            HardwareType::ProteonPronetTokenRing => 4,
2575            HardwareType::Chaos => 5,
2576            HardwareType::Ieee802Networks => 6,
2577            HardwareType::Arcnet => 7,
2578            HardwareType::Hyperchannel => 8,
2579            HardwareType::Lanstar => 9,
2580            HardwareType::AutonetShortAddress => 10,
2581            HardwareType::LocalTalk => 11,
2582            HardwareType::LocalNet => 12,
2583            HardwareType::UltraLink => 13,
2584            HardwareType::SMDS => 14,
2585            HardwareType::FrameRelay => 15,
2586            HardwareType::ATM => 16,
2587            HardwareType::HDLC => 17,
2588            HardwareType::FibreChannel => 18,
2589            HardwareType::ATMLogical => 19,
2590            HardwareType::SerialLine => 20,
2591            HardwareType::ATMPhysical => 21,
2592            HardwareType::MilStd188220 => 22,
2593            HardwareType::Metricom => 23,
2594            HardwareType::IEEE1394 => 24,
2595            HardwareType::MAPOS => 25,
2596            HardwareType::Twinaxial => 26,
2597            HardwareType::EUI64 => 27,
2598            HardwareType::HIPARP => 28,
2599            HardwareType::IPAndARPOverISO => 29,
2600            HardwareType::ARPSec => 30,
2601            HardwareType::IPsecTunnel => 31,
2602            HardwareType::InfiniBand => 32,
2603            HardwareType::TIA102 => 33,
2604            HardwareType::Wiegand => 34,
2605            HardwareType::PureIP => 35,
2606            HardwareType::HwExp1 => 36,
2607            HardwareType::Hf1 => 37,
2608            HardwareType::UnifiedBus => 38,
2609            HardwareType::HwExp2 => 256,
2610            HardwareType::AEthernet => 257,
2611            HardwareType::Unknown(val) => val,
2612        }
2613    }
2614}
2615
2616#[derive(Copy, Clone, Debug, PartialEq)]
2617pub struct NetworkInterface {
2618    pub interface_id: u32,
2619    pub hardware_type: HardwareType,
2620}
2621
2622#[derive(Copy, Clone, Debug, PartialEq)]
2623pub enum StaticConfigType {
2624    NoStaticConfig = 0x00,
2625    StaticConfigIpv4 = 0x01,
2626    StaticConfigIpv6 = 0x02,
2627}
2628
2629impl TryFrom<u8> for StaticConfigType {
2630    type Error = RdmError;
2631
2632    fn try_from(value: u8) -> Result<Self, Self::Error> {
2633        match value {
2634            0x00 => Ok(Self::NoStaticConfig),
2635            0x01 => Ok(Self::StaticConfigIpv4),
2636            0x02 => Ok(Self::StaticConfigIpv6),
2637            value => Err(RdmError::InvalidStaticConfigType(value)),
2638        }
2639    }
2640}
2641
2642#[derive(Copy, Clone, Debug, PartialEq)]
2643pub enum BrokerState {
2644    Disabled = 0x00,
2645    Active = 0x01,
2646    Standby = 0x02,
2647}
2648
2649impl TryFrom<u8> for BrokerState {
2650    type Error = RdmError;
2651
2652    fn try_from(value: u8) -> Result<Self, Self::Error> {
2653        match value {
2654            0x00 => Ok(Self::Disabled),
2655            0x01 => Ok(Self::Active),
2656            0x02 => Ok(Self::Standby),
2657            value => Err(RdmError::InvalidBrokerState(value)),
2658        }
2659    }
2660}
2661
2662#[derive(Copy, Clone, Debug, PartialEq)]
2663pub enum DiscoveryState {
2664    Incomplete,
2665    Incremental,
2666    Full,
2667    NotActive,
2668    ManufacturerSpecific(u8),
2669}
2670
2671impl TryFrom<u8> for DiscoveryState {
2672    type Error = RdmError;
2673
2674    fn try_from(value: u8) -> Result<Self, Self::Error> {
2675        match value {
2676            0x00 => Ok(Self::Incomplete),
2677            0x01 => Ok(Self::Incremental),
2678            0x02 => Ok(Self::Full),
2679            0x04 => Ok(Self::NotActive),
2680            value if (0x80..=0xff).contains(&value) => Ok(Self::ManufacturerSpecific(value)),
2681            value => Err(RdmError::InvalidDiscoveryState(value)),
2682        }
2683    }
2684}
2685
2686impl From<DiscoveryState> for u8 {
2687    fn from(value: DiscoveryState) -> u8 {
2688        match value {
2689            DiscoveryState::Incomplete => 0x00,
2690            DiscoveryState::Incremental => 0x01,
2691            DiscoveryState::Full => 0x02,
2692            DiscoveryState::NotActive => 0x04,
2693            DiscoveryState::ManufacturerSpecific(value) => value,
2694        }
2695    }
2696}
2697
2698#[derive(Copy, Clone, Debug, PartialEq)]
2699pub enum DiscoveryCountStatus {
2700    Incomplete,
2701    Count(u16),
2702    Unknown
2703}
2704
2705impl From<u16> for DiscoveryCountStatus {
2706    fn from(value: u16) -> Self {
2707        match value {
2708            0x0000 => Self::Incomplete,
2709            0xffff => Self::Unknown,
2710            value => Self::Count(value),
2711        }
2712    }
2713}
2714
2715impl From<DiscoveryCountStatus> for u16 {
2716    fn from(value: DiscoveryCountStatus) -> u16 {
2717        match value {
2718            DiscoveryCountStatus::Incomplete => 0x0000,
2719            DiscoveryCountStatus::Unknown => 0xffff,
2720            DiscoveryCountStatus::Count(value) => value,
2721        }
2722    }
2723}
2724
2725#[derive(Copy, Clone, Debug, PartialEq)]
2726pub enum EndpointMode {
2727    Disabled = 0x00, // Does not pass any DMX512-A/RDM traffic on a local RDM Command Port or DMX512-A Data Link
2728    Input = 0x01, // Receives DMX512-A/RDM data on a local RDM Responder Port or DMX512-A Data Link
2729    Output = 0x02, // Sends DMX512-A/RDM data on a local Command Port or DMX512-A Data Link
2730}
2731
2732impl TryFrom<u8> for EndpointMode {
2733    type Error = RdmError;
2734
2735    fn try_from(value: u8) -> Result<Self, Self::Error> {
2736        match value {
2737            0x00 => Ok(Self::Disabled),
2738            0x01 => Ok(Self::Input),
2739            0x02 => Ok(Self::Output),
2740            value => Err(RdmError::InvalidEndpointMode(value)),
2741        }
2742    }
2743}
2744
2745#[derive(Copy, Clone, Debug, PartialEq)]
2746pub enum EndpointId {
2747    Null,
2748    Device(u16),
2749    Reserved(u16),
2750    Broadcast,
2751}
2752
2753impl From<u16> for EndpointId {
2754    fn from(value: u16) -> Self {
2755        match value {
2756            0 => EndpointId::Null,
2757            0xffff => EndpointId::Broadcast,
2758            value if (0xfa00..=0xfffe).contains(&value) => EndpointId::Reserved(value),
2759            value => EndpointId::Device(value),
2760        }
2761    }
2762}
2763
2764impl From<EndpointId> for u16 {
2765    fn from(value: EndpointId) -> Self {
2766        match value {
2767            EndpointId::Broadcast => 0xffff,
2768            EndpointId::Null => 0,
2769            EndpointId::Device(value) => value,
2770            EndpointId::Reserved(value) => value,
2771        }
2772    }
2773}
2774
2775#[derive(Copy, Clone, Debug, PartialEq)]
2776pub enum EndpointType {
2777    Virtual = 0x00,
2778    Physical = 0x01
2779}
2780
2781impl TryFrom<u8> for EndpointType {
2782    type Error = RdmError;
2783
2784    fn try_from(value: u8) -> Result<Self, Self::Error> {
2785        match value {
2786            0x00 => Ok(Self::Virtual),
2787            0x01 => Ok(Self::Physical),
2788            value => Err(RdmError::InvalidEndpointType(value)),
2789        }
2790    }
2791}
2792
2793#[cfg(test)]
2794mod tests {
2795    use super::*;
2796
2797    #[test]
2798    #[cfg(feature = "alloc")]
2799    fn should_decode_string_bytes() {
2800        assert_eq!(
2801            decode_string_bytes(&b"null terminated string\0"[..]).unwrap(),
2802            "null terminated string".to_string()
2803        );
2804        assert_eq!(
2805            decode_string_bytes(&b"not null terminated string"[..]).unwrap(),
2806            "not null terminated string".to_string()
2807        );
2808        assert_eq!(
2809            decode_string_bytes(&b"early terminated\0string"[..]).unwrap(),
2810            "early terminated".to_string()
2811        );
2812    }
2813
2814    #[test]
2815    #[cfg(not(feature = "alloc"))]
2816    fn should_decode_string_bytes() {
2817        assert_eq!(
2818            decode_string_bytes::<32>(&b"null terminated string\0"[..]).unwrap(),
2819            String::from_utf8(Vec::<u8, 32>::from_slice(b"null terminated string").unwrap())
2820                .unwrap()
2821        );
2822        assert_eq!(
2823            decode_string_bytes::<32>(&b"not null terminated string"[..]).unwrap(),
2824            String::from_utf8(Vec::<u8, 32>::from_slice(b"not null terminated string").unwrap())
2825                .unwrap()
2826        );
2827        assert_eq!(
2828            decode_string_bytes::<32>(&b"early terminated\0string"[..]).unwrap(),
2829            String::from_utf8(Vec::<u8, 32>::from_slice(b"early terminated").unwrap()).unwrap()
2830        );
2831    }
2832}