dmx512_rdm_protocol/rdm/
response.rs

1//! Data types and functionality for decoding RDM responses
2//!
3//! ### RdmResponse
4//!
5//! ```rust
6//! use dmx512_rdm_protocol::rdm::{
7//!     parameter::ParameterId,
8//!     response::{
9//!         RdmFrameResponse, RdmResponse, ResponseData, ResponseParameterData, ResponseType,
10//!     },
11//!     CommandClass, DeviceUID, SubDeviceId,
12//! };
13//!
14//! let decoded = RdmResponse::decode(&[
15//!     0xcc, // Start Code
16//!     0x01, // Sub Start Code
17//!     0x19, // Message Length
18//!     0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
19//!     0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
20//!     0x00, // Transaction Number
21//!     0x00, // Response Type = Ack
22//!     0x00, // Message Count
23//!     0x00, 0x00, // Sub-Device ID = Root Device
24//!     0x21, // Command Class = GetCommandResponse
25//!     0x10, 0x00, // Parameter ID = Identify Device
26//!     0x01, // PDL
27//!     0x01, // Identifying = true
28//!     0x01, 0x43, // Checksum
29//! ]);
30//!
31//! let expected = Ok(RdmResponse::RdmFrame(RdmFrameResponse {
32//!     destination_uid: DeviceUID::new(0x0102, 0x03040506),
33//!     source_uid: DeviceUID::new(0x0605, 0x04030201),
34//!     transaction_number: 0x00,
35//!     response_type: ResponseType::Ack,
36//!     message_count: 0x00,
37//!     sub_device_id: SubDeviceId::RootDevice,
38//!     command_class: CommandClass::GetCommandResponse,
39//!     parameter_id: ParameterId::IdentifyDevice,
40//!     parameter_data: ResponseData::ParameterData(Some(
41//!         ResponseParameterData::GetIdentifyDevice(true),
42//!     )),
43//! }));
44//!
45//! assert_eq!(decoded, expected);
46//! ```
47
48use super::{
49    bsd_16_crc,
50    parameter::{
51        decode_string_bytes, BrokerState, DefaultSlotValue, DhcpMode, DiscoveryCountStatus,
52        DiscoveryState, DisplayInvertMode, EndpointId, EndpointMode, EndpointType, Ipv4Address,
53        Ipv4Route, Ipv6Address, LampOnMode, LampState, MergeMode, NetworkInterface,
54        ParameterDescription, ParameterId, PinCode, PowerState, PresetPlaybackMode,
55        PresetProgrammed, ProductCategory, ProductDetail, ProtocolVersion, SelfTest,
56        SensorDefinition, SensorValue, SlotInfo, StaticConfigType, StatusMessage, StatusType,
57        SupportedTimes, TimeMode,
58    },
59    CommandClass, DeviceUID, EncodedFrame, EncodedParameterData, RdmError, SubDeviceId,
60    DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE, DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE,
61    RDM_START_CODE_BYTE, RDM_SUB_START_CODE_BYTE,
62};
63use core::{fmt::Display, iter, result::Result};
64use macaddr::MacAddr6;
65
66#[cfg(not(feature = "alloc"))]
67use heapless::{String, Vec};
68
69#[derive(Copy, Clone, Debug, PartialEq)]
70pub enum ResponseNackReasonCode {
71    UnknownPid = 0x0000,
72    FormatError = 0x0001,
73    HardwareFault = 0x0002,
74    ProxyReject = 0x0003,
75    WriteProtect = 0x0004,
76    UnsupportedCommandClass = 0x0005,
77    DataOutOfRange = 0x0006,
78    BufferFull = 0x0007,
79    PacketSizeUnsupported = 0x0008,
80    SubDeviceIdOutOfRange = 0x0009,
81    ProxyBufferFull = 0x000a,
82    ActionNotSupported = 0x000b,
83    EndpointNumberInvalid = 0x000c,
84    InvalidEndpointMode = 0x000d,
85    UnknownUid = 0x000e,
86}
87
88impl TryFrom<u16> for ResponseNackReasonCode {
89    type Error = RdmError;
90
91    fn try_from(value: u16) -> Result<Self, RdmError> {
92        match value {
93            0x0000 => Ok(Self::UnknownPid),
94            0x0001 => Ok(Self::FormatError),
95            0x0002 => Ok(Self::HardwareFault),
96            0x0003 => Ok(Self::ProxyReject),
97            0x0004 => Ok(Self::WriteProtect),
98            0x0005 => Ok(Self::UnsupportedCommandClass),
99            0x0006 => Ok(Self::DataOutOfRange),
100            0x0007 => Ok(Self::BufferFull),
101            0x0008 => Ok(Self::PacketSizeUnsupported),
102            0x0009 => Ok(Self::SubDeviceIdOutOfRange),
103            0x000a => Ok(Self::ProxyBufferFull),
104            0x000b => Ok(Self::ActionNotSupported),
105            0x000c => Ok(Self::EndpointNumberInvalid),
106            0x000d => Ok(Self::InvalidEndpointMode),
107            0x000e => Ok(Self::UnknownUid),
108            value => Err(RdmError::InvalidNackReasonCode(value)),
109        }
110    }
111}
112
113impl Display for ResponseNackReasonCode {
114    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115        let message = match self {
116            Self::UnknownPid => "The responder cannot comply with request because the message is not implemented in responder.",
117            Self::FormatError => "The responder cannot interpret request as controller data was not formatted correctly.",
118            Self::HardwareFault => "The responder cannot comply due to an internal hardware fault.",
119            Self::ProxyReject => "Proxy is not the RDM line master and cannot comply with message.",
120            Self::WriteProtect => "Command normally allowed but being blocked currently.",
121            Self::UnsupportedCommandClass => "Not valid for Command Class attempted. May be used where GET allowed but SET is not supported.",
122            Self::DataOutOfRange => "Value for given Parameter out of allowable range or not supported.",
123            Self::BufferFull => "Buffer or Queue space currently has no free space to store data.",
124            Self::PacketSizeUnsupported => "Incoming message exceeds buffer capacity.",
125            Self::SubDeviceIdOutOfRange => "Sub-Device is out of range or unknown.",
126            Self::ProxyBufferFull => "The proxy buffer is full and can not store any more Queued Message or Status Message responses.",
127            Self::ActionNotSupported => "The parameter data is valid but the SET operation cannot be performed with the current configuration.",
128            Self::EndpointNumberInvalid => "The Endpoint Number is invalid.",
129            Self::InvalidEndpointMode => "The Endpoint Mode is invalid.",
130            Self::UnknownUid => "The UID is not known to the responder.",
131        };
132
133        f.write_str(message)
134    }
135}
136
137#[derive(Copy, Clone, Debug, PartialEq)]
138pub enum ResponseType {
139    Ack = 0x00,
140    AckTimer = 0x01,
141    NackReason = 0x02,
142    AckOverflow = 0x03,
143}
144
145impl TryFrom<u8> for ResponseType {
146    type Error = RdmError;
147
148    fn try_from(value: u8) -> Result<Self, Self::Error> {
149        match value {
150            0x00 => Ok(Self::Ack),
151            0x01 => Ok(Self::AckTimer),
152            0x02 => Ok(Self::NackReason),
153            0x03 => Ok(Self::AckOverflow),
154            _ => Err(RdmError::InvalidResponseType(value)),
155        }
156    }
157}
158
159#[allow(clippy::large_enum_variant)]
160#[derive(Clone, Debug, PartialEq)]
161pub enum ResponseData {
162    ParameterData(Option<ResponseParameterData>),
163    EstimateResponseTime(u16),
164    NackReason(ResponseNackReasonCode),
165}
166
167impl ResponseData {
168    pub fn encode(&self) -> EncodedParameterData {
169        #[cfg(feature = "alloc")]
170        let mut buf = Vec::new();
171
172        #[cfg(not(feature = "alloc"))]
173        let mut buf = Vec::new();
174
175        match self {
176            Self::ParameterData(Some(data)) => {
177                let data = data.encode();
178
179                #[cfg(feature = "alloc")]
180                buf.reserve(data.len());
181
182                buf.extend(data);
183            }
184            Self::ParameterData(None) => {}
185            Self::EstimateResponseTime(time) => {
186                #[cfg(feature = "alloc")]
187                buf.reserve(2);
188
189                buf.extend(time.to_be_bytes());
190            }
191            Self::NackReason(reason) => {
192                #[cfg(feature = "alloc")]
193                buf.reserve(2);
194
195                buf.extend((*reason as u16).to_be_bytes());
196            }
197        }
198
199        buf
200    }
201
202    pub fn decode(
203        response_type: ResponseType,
204        command_class: CommandClass,
205        parameter_data_length: u8,
206        parameter_id: ParameterId,
207        bytes: &[u8],
208    ) -> Result<Self, RdmError> {
209        match response_type {
210            ResponseType::Ack | ResponseType::AckOverflow => {
211                let parameter_data = if parameter_data_length > 0 {
212                    Some(ResponseParameterData::decode(
213                        command_class,
214                        parameter_id,
215                        bytes,
216                    )?)
217                } else {
218                    None
219                };
220
221                Ok(ResponseData::ParameterData(parameter_data))
222            }
223            ResponseType::AckTimer => {
224                let estimated_response_time = u16::from_be_bytes(bytes[0..=1].try_into()?);
225
226                Ok(ResponseData::EstimateResponseTime(estimated_response_time))
227            }
228            ResponseType::NackReason => {
229                let nack_reason = u16::from_be_bytes(bytes[0..=1].try_into()?).try_into()?;
230
231                Ok(ResponseData::NackReason(nack_reason))
232            }
233        }
234    }
235}
236
237#[allow(clippy::large_enum_variant)]
238#[non_exhaustive]
239#[derive(Clone, Debug, PartialEq)]
240pub enum ResponseParameterData {
241    // E1.20
242    DiscMute {
243        control_field: u16,
244        binding_uid: Option<DeviceUID>,
245    },
246    DiscUnMute {
247        control_field: u16,
248        binding_uid: Option<DeviceUID>,
249    },
250    GetProxiedDeviceCount {
251        device_count: u16,
252        list_change: bool,
253    },
254    GetProxiedDevices(
255        #[cfg(feature = "alloc")] Vec<DeviceUID>,
256        #[cfg(not(feature = "alloc"))] Vec<DeviceUID, 38>,
257    ),
258    GetCommsStatus {
259        short_message: u16,
260        length_mismatch: u16,
261        checksum_fail: u16,
262    },
263    GetStatusMessages(
264        #[cfg(feature = "alloc")] Vec<StatusMessage>,
265        #[cfg(not(feature = "alloc"))] Vec<StatusMessage, 25>,
266    ),
267    GetStatusIdDescription(
268        #[cfg(feature = "alloc")] String,
269        #[cfg(not(feature = "alloc"))] String<32>,
270    ),
271    GetSubDeviceIdStatusReportThreshold(StatusType),
272    GetSupportedParameters(
273        #[cfg(feature = "alloc")] Vec<u16>,
274        #[cfg(not(feature = "alloc"))] Vec<u16, 115>,
275    ),
276    GetParameterDescription(ParameterDescription),
277    GetDeviceInfo {
278        protocol_version: ProtocolVersion,
279        model_id: u16,
280        product_category: ProductCategory,
281        software_version_id: u32,
282        footprint: u16,
283        current_personality: u8,
284        personality_count: u8,
285        start_address: u16,
286        sub_device_count: u16,
287        sensor_count: u8,
288    },
289    GetProductDetailIdList(
290        #[cfg(feature = "alloc")] Vec<ProductDetail>,
291        #[cfg(not(feature = "alloc"))] Vec<ProductDetail, 115>,
292    ),
293    GetDeviceModelDescription(
294        #[cfg(feature = "alloc")] String,
295        #[cfg(not(feature = "alloc"))] String<32>,
296    ),
297    GetManufacturerLabel(
298        #[cfg(feature = "alloc")] String,
299        #[cfg(not(feature = "alloc"))] String<32>,
300    ),
301    GetDeviceLabel(
302        #[cfg(feature = "alloc")] String,
303        #[cfg(not(feature = "alloc"))] String<32>,
304    ),
305    GetFactoryDefaults(bool),
306    GetLanguageCapabilities(
307        #[cfg(feature = "alloc")] Vec<String>,
308        #[cfg(not(feature = "alloc"))] Vec<String<2>, 115>,
309    ),
310    GetLanguage(
311        #[cfg(feature = "alloc")] String,
312        #[cfg(not(feature = "alloc"))] String<2>,
313    ),
314    GetSoftwareVersionLabel(
315        #[cfg(feature = "alloc")] String,
316        #[cfg(not(feature = "alloc"))] String<32>,
317    ),
318    GetBootSoftwareVersionId(u32),
319    GetBootSoftwareVersionLabel(
320        #[cfg(feature = "alloc")] String,
321        #[cfg(not(feature = "alloc"))] String<32>,
322    ),
323    GetDmxPersonality {
324        current_personality: u8,
325        personality_count: u8,
326    },
327    GetDmxPersonalityDescription {
328        id: u8,
329        dmx_slots_required: u16,
330        #[cfg(feature = "alloc")]
331        description: String,
332        #[cfg(not(feature = "alloc"))]
333        description: String<32>,
334    },
335    GetDmxStartAddress(u16),
336    GetSlotInfo(
337        #[cfg(feature = "alloc")] Vec<SlotInfo>,
338        #[cfg(not(feature = "alloc"))] Vec<SlotInfo, 46>,
339    ),
340    GetSlotDescription {
341        slot_id: u16,
342        #[cfg(feature = "alloc")]
343        description: String,
344        #[cfg(not(feature = "alloc"))]
345        description: String<32>,
346    },
347    GetDefaultSlotValue(
348        #[cfg(feature = "alloc")] Vec<DefaultSlotValue>,
349        #[cfg(not(feature = "alloc"))] Vec<DefaultSlotValue, 77>,
350    ),
351    GetSensorDefinition(SensorDefinition),
352    GetSensorValue(SensorValue),
353    SetSensorValue(SensorValue),
354    GetDeviceHours(u32),
355    GetLampHours(u32),
356    GetLampStrikes(u32),
357    GetLampState(LampState),
358    GetLampOnMode(LampOnMode),
359    GetDevicePowerCycles(u32),
360    GetDisplayInvert(DisplayInvertMode),
361    GetDisplayLevel(u8),
362    GetPanInvert(bool),
363    GetTiltInvert(bool),
364    GetPanTiltSwap(bool),
365    GetRealTimeClock {
366        year: u16,
367        month: u8,
368        day: u8,
369        hour: u8,
370        minute: u8,
371        second: u8,
372    },
373    GetIdentifyDevice(bool),
374    GetPowerState(PowerState),
375    GetPerformSelfTest(bool),
376    GetSelfTestDescription {
377        self_test_id: SelfTest,
378        #[cfg(feature = "alloc")]
379        description: String,
380        #[cfg(not(feature = "alloc"))]
381        description: String<32>,
382    },
383    GetPresetPlayback {
384        mode: PresetPlaybackMode,
385        level: u8,
386    },
387    // E1.37-1
388    GetDmxBlockAddress {
389        total_sub_device_footprint: u16,
390        base_dmx_address: u16,
391    },
392    GetDmxFailMode {
393        scene_id: PresetPlaybackMode,
394        loss_of_signal_delay: TimeMode,
395        hold_time: TimeMode,
396        level: u8,
397    },
398    GetDmxStartupMode {
399        scene_id: PresetPlaybackMode,
400        startup_delay: TimeMode,
401        hold_time: TimeMode,
402        level: u8,
403    },
404    GetPowerOnSelfTest(bool),
405    GetLockState {
406        lock_state_id: u8,
407        lock_state_count: u8,
408    },
409    GetLockStateDescription {
410        lock_state_id: u8,
411        #[cfg(feature = "alloc")]
412        description: String,
413        #[cfg(not(feature = "alloc"))]
414        description: String<32>,
415    },
416    GetLockPin(PinCode),
417    GetBurnIn(u8),
418    GetDimmerInfo {
419        minimum_level_lower_limit: u16,
420        minimum_level_upper_limit: u16,
421        maximum_level_lower_limit: u16,
422        maximum_level_upper_limit: u16,
423        number_of_supported_curves: u8,
424        levels_resolution: u8,
425        minimum_level_split_levels_supported: bool,
426    },
427    GetMinimumLevel {
428        minimum_level_increasing: u16,
429        minimum_level_decreasing: u16,
430        on_below_minimum: bool,
431    },
432    GetMaximumLevel(u16),
433    GetCurve {
434        curve_id: u8,
435        curve_count: u8,
436    },
437    GetCurveDescription {
438        curve_id: u8,
439        #[cfg(feature = "alloc")]
440        description: String,
441        #[cfg(not(feature = "alloc"))]
442        description: String<32>,
443    },
444    GetOutputResponseTime {
445        response_time_id: u8,
446        response_time_count: u8,
447    },
448    GetOutputResponseTimeDescription {
449        response_time_id: u8,
450        #[cfg(feature = "alloc")]
451        description: String,
452        #[cfg(not(feature = "alloc"))]
453        description: String<32>,
454    },
455    GetModulationFrequency {
456        modulation_frequency_id: u8,
457        modulation_frequency_count: u8,
458    },
459    GetModulationFrequencyDescription {
460        modulation_frequency_id: u8,
461        frequency: u32,
462        #[cfg(feature = "alloc")]
463        description: String,
464        #[cfg(not(feature = "alloc"))]
465        description: String<32>,
466    },
467    GetPresetInfo {
468        level_field_supported: bool,
469        preset_sequence_supported: bool,
470        split_times_supported: bool,
471        dmx_fail_infinite_delay_time_supported: bool,
472        dmx_fail_infinite_hold_time_supported: bool,
473        startup_infinite_hold_time_supported: bool,
474        maximum_scene_number: u16,
475        minimum_preset_fade_time_supported: u16,
476        maximum_preset_fade_time_supported: u16,
477        minimum_preset_wait_time_supported: u16,
478        maximum_preset_wait_time_supported: u16,
479        minimum_dmx_fail_delay_time_supported: SupportedTimes,
480        maximum_dmx_fail_delay_time_supported: SupportedTimes,
481        minimum_dmx_fail_hold_time_supported: SupportedTimes,
482        maximum_dmx_fail_hold_time_supported: SupportedTimes,
483        minimum_startup_delay_time_supported: SupportedTimes,
484        maximum_startup_delay_time_supported: SupportedTimes,
485        minimum_startup_hold_time_supported: SupportedTimes,
486        maximum_startup_hold_time_supported: SupportedTimes,
487    },
488    GetPresetStatus {
489        scene_id: u16,
490        up_fade_time: u16,
491        down_fade_time: u16,
492        wait_time: u16,
493        programmed: PresetProgrammed,
494    },
495    GetPresetMergeMode(MergeMode),
496    // E1.37-2
497    GetListInterfaces(
498        #[cfg(feature = "alloc")] Vec<NetworkInterface>,
499        #[cfg(not(feature = "alloc"))] Vec<NetworkInterface, 38>,
500    ),
501    GetInterfaceLabel {
502        interface_id: u32,
503        #[cfg(feature = "alloc")]
504        interface_label: String,
505        #[cfg(not(feature = "alloc"))]
506        interface_label: String<32>,
507    },
508    GetInterfaceHardwareAddressType1 {
509        interface_id: u32,
510        hardware_address: MacAddr6,
511    },
512    GetIpV4DhcpMode {
513        interface_id: u32,
514        dhcp_mode: bool,
515    },
516    GetIpV4ZeroConfMode {
517        interface_id: u32,
518        zero_conf_mode: bool,
519    },
520    GetIpV4CurrentAddress {
521        interface_id: u32,
522        address: Ipv4Address,
523        netmask: u8,
524        dhcp_status: DhcpMode,
525    },
526    GetIpV4StaticAddress {
527        interface_id: u32,
528        address: Ipv4Address,
529        netmask: u8,
530    },
531    GetIpV4DefaultRoute {
532        interface_id: u32,
533        address: Ipv4Route,
534    },
535    GetDnsIpV4NameServer {
536        name_server_index: u8,
537        address: Ipv4Address,
538    },
539    GetDnsHostName(
540        #[cfg(feature = "alloc")] String,
541        #[cfg(not(feature = "alloc"))] String<63>,
542    ),
543    GetDnsDomainName(
544        #[cfg(feature = "alloc")] String,
545        #[cfg(not(feature = "alloc"))] String<32>,
546    ),
547    // E1.37-7
548    GetEndpointList {
549        list_change_number: u32,
550        #[cfg(feature = "alloc")]
551        endpoint_list: Vec<(EndpointId, EndpointType)>,
552        #[cfg(not(feature = "alloc"))]
553        endpoint_list: Vec<(EndpointId, EndpointType), 75>,
554    },
555    GetEndpointListChange {
556        list_change_number: u32,
557    },
558    GetIdentifyEndpoint {
559        endpoint_id: EndpointId,
560        identify: bool,
561    },
562    SetIdentifyEndpoint {
563        endpoint_id: EndpointId,
564    },
565    GetEndpointToUniverse {
566        endpoint_id: EndpointId,
567        universe: u16,
568    },
569    SetEndpointToUniverse {
570        endpoint_id: EndpointId,
571    },
572    GetEndpointMode {
573        endpoint_id: EndpointId,
574        mode: EndpointMode,
575    },
576    SetEndpointMode {
577        endpoint_id: EndpointId,
578    },
579    GetEndpointLabel {
580        endpoint_id: EndpointId,
581        #[cfg(feature = "alloc")]
582        label: String,
583        #[cfg(not(feature = "alloc"))]
584        label: String<32>,
585    },
586    SetEndpointLabel {
587        endpoint_id: EndpointId,
588    },
589    GetRdmTrafficEnable {
590        endpoint_id: EndpointId,
591        enable: bool,
592    },
593    SetRdmTrafficEnable {
594        endpoint_id: EndpointId,
595    },
596    GetDiscoveryState {
597        endpoint_id: EndpointId,
598        device_count: DiscoveryCountStatus,
599        discovery_state: DiscoveryState,
600    },
601    SetDiscoveryState {
602        endpoint_id: EndpointId,
603    },
604    GetBackgroundDiscovery {
605        endpoint_id: EndpointId,
606        enabled: bool,
607    },
608    SetBackgroundDiscovery {
609        endpoint_id: EndpointId,
610    },
611    GetEndpointTiming {
612        endpoint_id: EndpointId,
613        current_setting_id: u8,
614        setting_count: u8,
615    },
616    SetEndpointTiming {
617        endpoint_id: EndpointId,
618    },
619    GetEndpointTimingDescription {
620        setting_id: u8,
621        #[cfg(feature = "alloc")]
622        description: String,
623        #[cfg(not(feature = "alloc"))]
624        description: String<32>,
625    },
626    GetEndpointResponders {
627        endpoint_id: EndpointId,
628        list_change_number: u32,
629        #[cfg(feature = "alloc")]
630        responders: Vec<DeviceUID>,
631        #[cfg(not(feature = "alloc"))]
632        responders: Vec<DeviceUID, 37>,
633    },
634    GetEndpointResponderListChange {
635        endpoint_id: EndpointId,
636        list_change_number: u32,
637    },
638    GetBindingControlFields {
639        endpoint_id: EndpointId,
640        uid: DeviceUID,
641        control_field: u16,
642        binding_uid: DeviceUID,
643    },
644    GetBackgroundQueuedStatusPolicy {
645        current_policy_id: u8,
646        policy_count: u8,
647    },
648    GetBackgroundQueuedStatusPolicyDescription {
649        policy_id: u8,
650        #[cfg(feature = "alloc")]
651        description: String,
652        #[cfg(not(feature = "alloc"))]
653        description: String<32>,
654    },
655    // E1.33
656    GetComponentScope {
657        scope_slot: u16,
658        #[cfg(feature = "alloc")]
659        scope_string: String,
660        #[cfg(not(feature = "alloc"))]
661        scope_string: String<63>,
662        static_config_type: StaticConfigType,
663        static_ipv4_address: Ipv4Address,
664        static_ipv6_address: Ipv6Address,
665        static_port: u16,
666    },
667    GetSearchDomain(
668        #[cfg(feature = "alloc")] String,
669        #[cfg(not(feature = "alloc"))] String<231>,
670    ),
671    GetTcpCommsStatus {
672        #[cfg(feature = "alloc")]
673        scope_string: String,
674        #[cfg(not(feature = "alloc"))]
675        scope_string: String<231>,
676        broker_ipv4_address: Ipv4Address,
677        broker_ipv6_address: Ipv6Address,
678        broker_port: u16,
679        unhealthy_tcp_events: u16,
680    },
681    GetBrokerStatus {
682        is_allowing_set_commands: bool,
683        broker_state: BrokerState,
684    },
685    ManufacturerSpecific(
686        #[cfg(feature = "alloc")] Vec<u8>,
687        #[cfg(not(feature = "alloc"))] Vec<u8, 231>,
688    ),
689    Unsupported(
690        #[cfg(feature = "alloc")] Vec<u8>,
691        #[cfg(not(feature = "alloc"))] Vec<u8, 231>,
692    ),
693}
694
695impl ResponseParameterData {
696    pub fn encode(&self) -> EncodedParameterData {
697        #[cfg(feature = "alloc")]
698        let mut buf = Vec::new();
699
700        #[cfg(not(feature = "alloc"))]
701        let mut buf = Vec::new();
702
703        match self {
704            Self::DiscMute {
705                control_field,
706                binding_uid,
707            } => {
708                #[cfg(feature = "alloc")]
709                buf.reserve(0x0e);
710
711                buf.extend(control_field.to_be_bytes());
712
713                if let Some(binding_uid) = binding_uid {
714                    buf.extend(binding_uid.manufacturer_id.to_be_bytes());
715                    buf.extend(binding_uid.device_id.to_be_bytes());
716                }
717            }
718            Self::DiscUnMute {
719                control_field,
720                binding_uid,
721            } => {
722                #[cfg(feature = "alloc")]
723                buf.reserve(0x0e);
724
725                buf.extend(control_field.to_be_bytes());
726
727                if let Some(binding_uid) = binding_uid {
728                    buf.extend(binding_uid.manufacturer_id.to_be_bytes());
729                    buf.extend(binding_uid.device_id.to_be_bytes());
730                }
731            }
732            Self::GetProxiedDeviceCount {
733                device_count,
734                list_change,
735            } => {
736                #[cfg(feature = "alloc")]
737                buf.reserve(0x03);
738
739                buf.extend(device_count.to_be_bytes());
740
741                #[cfg(feature = "alloc")]
742                buf.push(*list_change as u8);
743                #[cfg(not(feature = "alloc"))]
744                buf.push(*list_change as u8).unwrap();
745            }
746            Self::GetProxiedDevices(devices) => {
747                #[cfg(feature = "alloc")]
748                buf.reserve(devices.len() * 6);
749
750                for device in devices {
751                    buf.extend(device.manufacturer_id.to_be_bytes());
752                    buf.extend(device.device_id.to_be_bytes());
753                }
754            }
755            Self::GetCommsStatus {
756                short_message,
757                length_mismatch,
758                checksum_fail,
759            } => {
760                #[cfg(feature = "alloc")]
761                buf.reserve(6);
762
763                buf.extend(short_message.to_be_bytes());
764                buf.extend(length_mismatch.to_be_bytes());
765                buf.extend(checksum_fail.to_be_bytes());
766            }
767            Self::GetStatusMessages(messages) => {
768                for message in messages {
769                    buf.extend(u16::from(message.sub_device_id).to_be_bytes());
770
771                    #[cfg(feature = "alloc")]
772                    buf.push(message.status_type as u8);
773                    #[cfg(not(feature = "alloc"))]
774                    buf.push(message.status_type as u8).unwrap();
775
776                    buf.extend(message.status_message_id.to_be_bytes());
777                    buf.extend(message.data_value1.to_be_bytes());
778                    buf.extend(message.data_value2.to_be_bytes());
779
780                    if let Some(description) = &message.description {
781                        buf.extend(description.bytes());
782                    }
783                }
784            }
785            Self::GetStatusIdDescription(description) => {
786                #[cfg(feature = "alloc")]
787                buf.reserve(description.len());
788
789                buf.extend(description.bytes());
790            }
791            Self::GetSubDeviceIdStatusReportThreshold(status) => {
792                #[cfg(feature = "alloc")]
793                buf.reserve(1);
794
795                #[cfg(feature = "alloc")]
796                buf.push(*status as u8);
797                #[cfg(not(feature = "alloc"))]
798                buf.push(*status as u8).unwrap();
799            }
800            Self::GetSupportedParameters(parameters) => {
801                #[cfg(feature = "alloc")]
802                buf.reserve(parameters.len() * 2);
803
804                for parameter in parameters {
805                    buf.extend(parameter.to_be_bytes());
806                }
807            }
808            Self::GetParameterDescription(description) => {
809                #[cfg(feature = "alloc")]
810                buf.reserve(20 + description.description.len());
811
812                buf.extend(description.parameter_id.to_be_bytes());
813
814                #[cfg(feature = "alloc")]
815                buf.push(description.parameter_data_length);
816                #[cfg(not(feature = "alloc"))]
817                buf.push(description.parameter_data_length).unwrap();
818
819                #[cfg(feature = "alloc")]
820                buf.push(description.data_type.into());
821                #[cfg(not(feature = "alloc"))]
822                buf.push(description.data_type.into()).unwrap();
823
824                #[cfg(feature = "alloc")]
825                buf.push(description.command_class as u8);
826                #[cfg(not(feature = "alloc"))]
827                buf.push(description.command_class as u8).unwrap();
828
829                #[cfg(feature = "alloc")]
830                buf.push(description.command_class as u8);
831                #[cfg(not(feature = "alloc"))]
832                buf.push(description.command_class as u8).unwrap();
833
834                #[cfg(feature = "alloc")]
835                buf.push(description.unit_type.into());
836                #[cfg(not(feature = "alloc"))]
837                buf.push(description.unit_type.into()).unwrap();
838
839                #[cfg(feature = "alloc")]
840                buf.push(description.prefix as u8);
841                #[cfg(not(feature = "alloc"))]
842                buf.push(description.prefix as u8).unwrap();
843
844                buf.extend(description.raw_minimum_valid_value);
845                buf.extend(description.raw_maximum_valid_value);
846                buf.extend(description.raw_default_value);
847
848                #[cfg(feature = "alloc")]
849                buf.extend(description.description.bytes());
850                #[cfg(not(feature = "alloc"))]
851                buf.extend(description.description.bytes());
852            }
853            Self::GetDeviceInfo {
854                protocol_version,
855                model_id,
856                product_category,
857                software_version_id,
858                footprint,
859                current_personality,
860                personality_count,
861                start_address,
862                sub_device_count,
863                sensor_count,
864            } => {
865                #[cfg(feature = "alloc")]
866                buf.reserve(21);
867
868                buf.extend(u16::from(*protocol_version).to_be_bytes());
869
870                buf.extend(model_id.to_be_bytes());
871                buf.extend(u16::from(*product_category).to_be_bytes());
872                buf.extend(software_version_id.to_be_bytes());
873                buf.extend(footprint.to_be_bytes());
874
875                #[cfg(feature = "alloc")]
876                buf.push(*current_personality);
877                #[cfg(not(feature = "alloc"))]
878                buf.push(*current_personality).unwrap();
879
880                #[cfg(feature = "alloc")]
881                buf.push(*personality_count);
882                #[cfg(not(feature = "alloc"))]
883                buf.push(*personality_count).unwrap();
884
885                buf.extend(start_address.to_be_bytes());
886                buf.extend(sub_device_count.to_be_bytes());
887
888                #[cfg(feature = "alloc")]
889                buf.push(*sensor_count);
890                #[cfg(not(feature = "alloc"))]
891                buf.push(*sensor_count).unwrap();
892            }
893            Self::GetProductDetailIdList(details) => {
894                #[cfg(feature = "alloc")]
895                buf.reserve(details.len() * 2);
896
897                for &detail in details {
898                    buf.extend(u16::from(detail).to_be_bytes());
899                }
900            }
901            Self::GetDeviceModelDescription(description) => {
902                #[cfg(feature = "alloc")]
903                buf.reserve(description.len());
904
905                buf.extend(description.bytes());
906            }
907            Self::GetManufacturerLabel(label) => {
908                #[cfg(feature = "alloc")]
909                buf.reserve(label.len());
910
911                buf.extend(label.bytes());
912            }
913            Self::GetDeviceLabel(label) => {
914                #[cfg(feature = "alloc")]
915                buf.reserve(label.len());
916
917                buf.extend(label.bytes());
918            }
919            Self::GetFactoryDefaults(defaults) => {
920                #[cfg(feature = "alloc")]
921                buf.reserve(1);
922
923                #[cfg(feature = "alloc")]
924                buf.push(*defaults as u8);
925                #[cfg(not(feature = "alloc"))]
926                buf.push(*defaults as u8).unwrap();
927            }
928            Self::GetLanguageCapabilities(languages) => {
929                #[cfg(feature = "alloc")]
930                buf.reserve(languages.len() * 2);
931
932                for language in languages {
933                    buf.extend(language.bytes());
934                }
935            }
936            Self::GetLanguage(language) => {
937                #[cfg(feature = "alloc")]
938                buf.reserve(2);
939
940                buf.extend(language.bytes());
941            }
942            Self::GetSoftwareVersionLabel(label) => {
943                #[cfg(feature = "alloc")]
944                buf.reserve(label.len());
945
946                buf.extend(label.bytes());
947            }
948            Self::GetBootSoftwareVersionId(version_id) => {
949                #[cfg(feature = "alloc")]
950                buf.reserve(4);
951
952                buf.extend(version_id.to_be_bytes());
953            }
954            Self::GetBootSoftwareVersionLabel(label) => {
955                #[cfg(feature = "alloc")]
956                buf.reserve(label.len());
957
958                buf.extend(label.bytes());
959            }
960            Self::GetDmxPersonality {
961                current_personality,
962                personality_count,
963            } => {
964                #[cfg(feature = "alloc")]
965                buf.reserve(2);
966
967                #[cfg(feature = "alloc")]
968                buf.push(*current_personality);
969                #[cfg(not(feature = "alloc"))]
970                buf.push(*current_personality).unwrap();
971
972                #[cfg(feature = "alloc")]
973                buf.push(*personality_count);
974                #[cfg(not(feature = "alloc"))]
975                buf.push(*personality_count).unwrap();
976            }
977            Self::GetDmxPersonalityDescription {
978                id,
979                dmx_slots_required,
980                description,
981            } => {
982                #[cfg(feature = "alloc")]
983                buf.reserve(3 + description.len());
984
985                #[cfg(feature = "alloc")]
986                buf.push(*id);
987                #[cfg(not(feature = "alloc"))]
988                buf.push(*id).unwrap();
989
990                buf.extend(dmx_slots_required.to_be_bytes());
991                buf.extend(description.bytes());
992            }
993            Self::GetDmxStartAddress(address) => {
994                #[cfg(feature = "alloc")]
995                buf.reserve(2);
996
997                buf.extend(address.to_be_bytes());
998            }
999            Self::GetSlotInfo(slots) => {
1000                #[cfg(feature = "alloc")]
1001                buf.reserve(slots.len() * 5);
1002
1003                for slot in slots {
1004                    buf.extend(slot.id.to_be_bytes());
1005
1006                    #[cfg(feature = "alloc")]
1007                    buf.push(slot.r#type.into());
1008                    #[cfg(not(feature = "alloc"))]
1009                    buf.push(slot.r#type.into()).unwrap();
1010
1011                    buf.extend(slot.label_id.to_be_bytes());
1012                }
1013            }
1014            Self::GetSlotDescription {
1015                slot_id,
1016                description,
1017            } => {
1018                #[cfg(feature = "alloc")]
1019                buf.reserve(2 + description.len());
1020
1021                buf.extend(slot_id.to_be_bytes());
1022                buf.extend(description.bytes());
1023            }
1024            Self::GetDefaultSlotValue(values) => {
1025                #[cfg(feature = "alloc")]
1026                buf.reserve(values.len() * 3);
1027
1028                for slot in values {
1029                    buf.extend(slot.id.to_be_bytes());
1030
1031                    #[cfg(feature = "alloc")]
1032                    buf.push(slot.value);
1033                    #[cfg(not(feature = "alloc"))]
1034                    buf.push(slot.value).unwrap();
1035                }
1036            }
1037            Self::GetSensorDefinition(definition) => {
1038                #[cfg(feature = "alloc")]
1039                buf.reserve(14 + definition.description.len());
1040
1041                #[cfg(feature = "alloc")]
1042                buf.push(definition.kind.into());
1043                #[cfg(not(feature = "alloc"))]
1044                buf.push(definition.kind.into()).unwrap();
1045
1046                #[cfg(feature = "alloc")]
1047                buf.push(definition.unit.into());
1048                #[cfg(not(feature = "alloc"))]
1049                buf.push(definition.unit.into()).unwrap();
1050
1051                #[cfg(feature = "alloc")]
1052                buf.push(definition.prefix as u8);
1053                #[cfg(not(feature = "alloc"))]
1054                buf.push(definition.prefix as u8).unwrap();
1055
1056                #[cfg(feature = "alloc")]
1057                buf.push(definition.prefix as u8);
1058                #[cfg(not(feature = "alloc"))]
1059                buf.push(definition.prefix as u8).unwrap();
1060
1061                buf.extend(definition.range_minimum_value.to_be_bytes());
1062                buf.extend(definition.range_maximum_value.to_be_bytes());
1063                buf.extend(definition.normal_minimum_value.to_be_bytes());
1064                buf.extend(definition.normal_maximum_value.to_be_bytes());
1065
1066                #[cfg(feature = "alloc")]
1067                buf.push(definition.is_lowest_highest_detected_value_supported as u8);
1068                #[cfg(not(feature = "alloc"))]
1069                buf.push(definition.is_lowest_highest_detected_value_supported as u8)
1070                    .unwrap();
1071
1072                #[cfg(feature = "alloc")]
1073                buf.push(definition.is_recorded_value_supported as u8);
1074                #[cfg(not(feature = "alloc"))]
1075                buf.push(definition.is_recorded_value_supported as u8)
1076                    .unwrap();
1077
1078                buf.extend(definition.description.bytes());
1079            }
1080            Self::GetSensorValue(sensor_value) => {
1081                #[cfg(feature = "alloc")]
1082                buf.reserve(9);
1083
1084                #[cfg(feature = "alloc")]
1085                buf.push(sensor_value.sensor_id);
1086                #[cfg(not(feature = "alloc"))]
1087                buf.push(sensor_value.sensor_id).unwrap();
1088
1089                buf.extend(sensor_value.current_value.to_be_bytes());
1090                buf.extend(sensor_value.lowest_detected_value.to_be_bytes());
1091                buf.extend(sensor_value.highest_detected_value.to_be_bytes());
1092                buf.extend(sensor_value.recorded_value.to_be_bytes());
1093            }
1094            Self::SetSensorValue(sensor_value) => {
1095                #[cfg(feature = "alloc")]
1096                buf.reserve(9);
1097
1098                #[cfg(feature = "alloc")]
1099                buf.push(sensor_value.sensor_id);
1100                #[cfg(not(feature = "alloc"))]
1101                buf.push(sensor_value.sensor_id).unwrap();
1102
1103                buf.extend(sensor_value.current_value.to_be_bytes());
1104                buf.extend(sensor_value.lowest_detected_value.to_be_bytes());
1105                buf.extend(sensor_value.highest_detected_value.to_be_bytes());
1106                buf.extend(sensor_value.recorded_value.to_be_bytes());
1107            }
1108            Self::GetDeviceHours(hours) => {
1109                #[cfg(feature = "alloc")]
1110                buf.reserve(4);
1111
1112                buf.extend(hours.to_be_bytes());
1113            }
1114            Self::GetLampHours(hours) => {
1115                #[cfg(feature = "alloc")]
1116                buf.reserve(4);
1117
1118                buf.extend(hours.to_be_bytes());
1119            }
1120            Self::GetLampStrikes(strikes) => {
1121                #[cfg(feature = "alloc")]
1122                buf.reserve(4);
1123
1124                buf.extend(strikes.to_be_bytes());
1125            }
1126            Self::GetLampState(state) => {
1127                #[cfg(feature = "alloc")]
1128                buf.reserve(1);
1129
1130                #[cfg(feature = "alloc")]
1131                buf.push((*state).into());
1132                #[cfg(not(feature = "alloc"))]
1133                buf.push((*state).into()).unwrap();
1134            }
1135            Self::GetLampOnMode(mode) => {
1136                #[cfg(feature = "alloc")]
1137                buf.reserve(1);
1138
1139                #[cfg(feature = "alloc")]
1140                buf.push((*mode).into());
1141                #[cfg(not(feature = "alloc"))]
1142                buf.push((*mode).into()).unwrap();
1143            }
1144            Self::GetDevicePowerCycles(cycles) => {
1145                #[cfg(feature = "alloc")]
1146                buf.reserve(4);
1147
1148                buf.extend(cycles.to_be_bytes());
1149            }
1150            Self::GetDisplayInvert(mode) => {
1151                #[cfg(feature = "alloc")]
1152                buf.reserve(1);
1153
1154                #[cfg(feature = "alloc")]
1155                buf.push(*mode as u8);
1156                #[cfg(not(feature = "alloc"))]
1157                buf.push(*mode as u8).unwrap();
1158            }
1159            Self::GetDisplayLevel(level) => {
1160                #[cfg(feature = "alloc")]
1161                buf.reserve(1);
1162
1163                #[cfg(feature = "alloc")]
1164                buf.push(*level);
1165                #[cfg(not(feature = "alloc"))]
1166                buf.push(*level).unwrap();
1167            }
1168            Self::GetPanInvert(invert) => {
1169                #[cfg(feature = "alloc")]
1170                buf.reserve(1);
1171
1172                #[cfg(feature = "alloc")]
1173                buf.push(*invert as u8);
1174                #[cfg(not(feature = "alloc"))]
1175                buf.push(*invert as u8).unwrap();
1176            }
1177            Self::GetTiltInvert(invert) => {
1178                #[cfg(feature = "alloc")]
1179                buf.reserve(1);
1180
1181                #[cfg(feature = "alloc")]
1182                buf.push(*invert as u8);
1183                #[cfg(not(feature = "alloc"))]
1184                buf.push(*invert as u8).unwrap();
1185            }
1186            Self::GetPanTiltSwap(swap) => {
1187                #[cfg(feature = "alloc")]
1188                buf.reserve(1);
1189
1190                #[cfg(feature = "alloc")]
1191                buf.push(*swap as u8);
1192                #[cfg(not(feature = "alloc"))]
1193                buf.push(*swap as u8).unwrap();
1194            }
1195            Self::GetRealTimeClock {
1196                year,
1197                month,
1198                day,
1199                hour,
1200                minute,
1201                second,
1202            } => {
1203                #[cfg(feature = "alloc")]
1204                buf.reserve(0x07);
1205
1206                buf.extend((*year).to_be_bytes());
1207
1208                #[cfg(feature = "alloc")]
1209                buf.push(*month);
1210                #[cfg(not(feature = "alloc"))]
1211                buf.push(*month).unwrap();
1212
1213                #[cfg(feature = "alloc")]
1214                buf.push(*day);
1215                #[cfg(not(feature = "alloc"))]
1216                buf.push(*day).unwrap();
1217
1218                #[cfg(feature = "alloc")]
1219                buf.push(*hour);
1220                #[cfg(not(feature = "alloc"))]
1221                buf.push(*hour).unwrap();
1222
1223                #[cfg(feature = "alloc")]
1224                buf.push(*minute);
1225                #[cfg(not(feature = "alloc"))]
1226                buf.push(*minute).unwrap();
1227
1228                #[cfg(feature = "alloc")]
1229                buf.push(*second);
1230                #[cfg(not(feature = "alloc"))]
1231                buf.push(*second).unwrap();
1232            }
1233            Self::GetIdentifyDevice(identifying) => {
1234                #[cfg(feature = "alloc")]
1235                buf.reserve(1);
1236
1237                #[cfg(feature = "alloc")]
1238                buf.push(*identifying as u8);
1239                #[cfg(not(feature = "alloc"))]
1240                buf.push(*identifying as u8).unwrap();
1241            }
1242            Self::GetPowerState(state) => {
1243                #[cfg(feature = "alloc")]
1244                buf.reserve(1);
1245
1246                #[cfg(feature = "alloc")]
1247                buf.push(*state as u8);
1248                #[cfg(not(feature = "alloc"))]
1249                buf.push(*state as u8).unwrap();
1250            }
1251            Self::GetPerformSelfTest(test) => {
1252                #[cfg(feature = "alloc")]
1253                buf.reserve(1);
1254
1255                #[cfg(feature = "alloc")]
1256                buf.push(*test as u8);
1257                #[cfg(not(feature = "alloc"))]
1258                buf.push(*test as u8).unwrap();
1259            }
1260            Self::GetSelfTestDescription {
1261                self_test_id,
1262                description,
1263            } => {
1264                #[cfg(feature = "alloc")]
1265                buf.reserve(1 + description.len());
1266
1267                #[cfg(feature = "alloc")]
1268                buf.push((*self_test_id).into());
1269                #[cfg(not(feature = "alloc"))]
1270                buf.push((*self_test_id).into()).unwrap();
1271
1272                buf.extend(description.bytes());
1273            }
1274            Self::GetPresetPlayback { mode, level } => {
1275                #[cfg(feature = "alloc")]
1276                buf.reserve(3);
1277
1278                buf.extend(u16::from(*mode).to_be_bytes());
1279
1280                #[cfg(feature = "alloc")]
1281                buf.push(*level);
1282                #[cfg(not(feature = "alloc"))]
1283                buf.push(*level).unwrap();
1284            }
1285            Self::GetDmxBlockAddress {
1286                total_sub_device_footprint,
1287                base_dmx_address,
1288            } => {
1289                #[cfg(feature = "alloc")]
1290                buf.reserve(4);
1291
1292                buf.extend(total_sub_device_footprint.to_be_bytes());
1293                buf.extend(base_dmx_address.to_be_bytes());
1294            }
1295            Self::GetDmxFailMode {
1296                scene_id,
1297                loss_of_signal_delay,
1298                hold_time,
1299                level,
1300            } => {
1301                #[cfg(feature = "alloc")]
1302                buf.reserve(7);
1303
1304                buf.extend(u16::from(*scene_id).to_be_bytes());
1305                buf.extend(u16::from(*loss_of_signal_delay).to_be_bytes());
1306                buf.extend(u16::from(*hold_time).to_be_bytes());
1307
1308                #[cfg(feature = "alloc")]
1309                buf.push(*level);
1310                #[cfg(not(feature = "alloc"))]
1311                buf.push(*level).unwrap();
1312            }
1313            Self::GetDmxStartupMode {
1314                scene_id,
1315                startup_delay,
1316                hold_time,
1317                level,
1318            } => {
1319                #[cfg(feature = "alloc")]
1320                buf.reserve(7);
1321
1322                buf.extend(u16::from(*scene_id).to_be_bytes());
1323                buf.extend(u16::from(*startup_delay).to_be_bytes());
1324                buf.extend(u16::from(*hold_time).to_be_bytes());
1325
1326                #[cfg(feature = "alloc")]
1327                buf.push(*level);
1328                #[cfg(not(feature = "alloc"))]
1329                buf.push(*level).unwrap();
1330            }
1331            Self::GetPowerOnSelfTest(test) => {
1332                #[cfg(feature = "alloc")]
1333                buf.reserve(1);
1334
1335                #[cfg(feature = "alloc")]
1336                buf.push(*test as u8);
1337                #[cfg(not(feature = "alloc"))]
1338                buf.push(*test as u8).unwrap();
1339            }
1340            Self::GetLockState {
1341                lock_state_id,
1342                lock_state_count,
1343            } => {
1344                #[cfg(feature = "alloc")]
1345                buf.reserve(2);
1346
1347                #[cfg(feature = "alloc")]
1348                buf.push(*lock_state_id);
1349                #[cfg(not(feature = "alloc"))]
1350                buf.push(*lock_state_id).unwrap();
1351
1352                #[cfg(feature = "alloc")]
1353                buf.push(*lock_state_count);
1354                #[cfg(not(feature = "alloc"))]
1355                buf.push(*lock_state_count).unwrap();
1356            }
1357            Self::GetLockStateDescription {
1358                lock_state_id,
1359                description,
1360            } => {
1361                #[cfg(feature = "alloc")]
1362                buf.reserve(1 + description.len());
1363
1364                #[cfg(feature = "alloc")]
1365                buf.push(*lock_state_id);
1366                #[cfg(not(feature = "alloc"))]
1367                buf.push(*lock_state_id).unwrap();
1368
1369                buf.extend(description.bytes());
1370            }
1371            Self::GetLockPin(pin) => {
1372                #[cfg(feature = "alloc")]
1373                buf.reserve(4);
1374
1375                buf.extend(pin.0.to_be_bytes());
1376            }
1377            Self::GetBurnIn(hours) => {
1378                #[cfg(feature = "alloc")]
1379                buf.reserve(1);
1380
1381                #[cfg(feature = "alloc")]
1382                buf.push(*hours);
1383                #[cfg(not(feature = "alloc"))]
1384                buf.push(*hours).unwrap();
1385            }
1386            Self::GetDimmerInfo {
1387                minimum_level_lower_limit,
1388                minimum_level_upper_limit,
1389                maximum_level_lower_limit,
1390                maximum_level_upper_limit,
1391                number_of_supported_curves,
1392                levels_resolution,
1393                minimum_level_split_levels_supported,
1394            } => {
1395                #[cfg(feature = "alloc")]
1396                buf.reserve(11);
1397
1398                buf.extend(minimum_level_lower_limit.to_be_bytes());
1399                buf.extend(minimum_level_upper_limit.to_be_bytes());
1400                buf.extend(maximum_level_lower_limit.to_be_bytes());
1401                buf.extend(maximum_level_upper_limit.to_be_bytes());
1402
1403                #[cfg(feature = "alloc")]
1404                buf.push(*number_of_supported_curves);
1405                #[cfg(not(feature = "alloc"))]
1406                buf.push(*number_of_supported_curves).unwrap();
1407
1408                #[cfg(feature = "alloc")]
1409                buf.push(*levels_resolution);
1410                #[cfg(not(feature = "alloc"))]
1411                buf.push(*levels_resolution).unwrap();
1412
1413                #[cfg(feature = "alloc")]
1414                buf.push(*minimum_level_split_levels_supported as u8);
1415                #[cfg(not(feature = "alloc"))]
1416                buf.push(*minimum_level_split_levels_supported as u8)
1417                    .unwrap();
1418            }
1419            Self::GetMinimumLevel {
1420                minimum_level_increasing,
1421                minimum_level_decreasing,
1422                on_below_minimum,
1423            } => {
1424                #[cfg(feature = "alloc")]
1425                buf.reserve(5);
1426
1427                buf.extend(minimum_level_increasing.to_be_bytes());
1428                buf.extend(minimum_level_decreasing.to_be_bytes());
1429
1430                #[cfg(feature = "alloc")]
1431                buf.push(*on_below_minimum as u8);
1432                #[cfg(not(feature = "alloc"))]
1433                buf.push(*on_below_minimum as u8).unwrap();
1434            }
1435            Self::GetMaximumLevel(level) => {
1436                #[cfg(feature = "alloc")]
1437                buf.reserve(2);
1438
1439                buf.extend(level.to_be_bytes());
1440            }
1441            Self::GetCurve {
1442                curve_id,
1443                curve_count,
1444            } => {
1445                #[cfg(feature = "alloc")]
1446                buf.reserve(2);
1447
1448                #[cfg(feature = "alloc")]
1449                buf.push(*curve_id);
1450                #[cfg(not(feature = "alloc"))]
1451                buf.push(*curve_id).unwrap();
1452
1453                #[cfg(feature = "alloc")]
1454                buf.push(*curve_count);
1455                #[cfg(not(feature = "alloc"))]
1456                buf.push(*curve_count).unwrap();
1457            }
1458            Self::GetCurveDescription {
1459                curve_id,
1460                description,
1461            } => {
1462                #[cfg(feature = "alloc")]
1463                buf.reserve(1 + description.len());
1464
1465                #[cfg(feature = "alloc")]
1466                buf.push(*curve_id);
1467                #[cfg(not(feature = "alloc"))]
1468                buf.push(*curve_id).unwrap();
1469
1470                buf.extend(description.bytes());
1471            }
1472            Self::GetOutputResponseTime {
1473                response_time_id,
1474                response_time_count,
1475            } => {
1476                #[cfg(feature = "alloc")]
1477                buf.reserve(2);
1478
1479                #[cfg(feature = "alloc")]
1480                buf.push(*response_time_id);
1481                #[cfg(not(feature = "alloc"))]
1482                buf.push(*response_time_id).unwrap();
1483
1484                #[cfg(feature = "alloc")]
1485                buf.push(*response_time_count);
1486                #[cfg(not(feature = "alloc"))]
1487                buf.push(*response_time_count).unwrap();
1488            }
1489            Self::GetOutputResponseTimeDescription {
1490                response_time_id,
1491                description,
1492            } => {
1493                #[cfg(feature = "alloc")]
1494                buf.reserve(1 + description.len());
1495
1496                #[cfg(feature = "alloc")]
1497                buf.push(*response_time_id);
1498                #[cfg(not(feature = "alloc"))]
1499                buf.push(*response_time_id).unwrap();
1500
1501                buf.extend(description.bytes());
1502            }
1503            Self::GetModulationFrequency {
1504                modulation_frequency_id,
1505                modulation_frequency_count,
1506            } => {
1507                #[cfg(feature = "alloc")]
1508                buf.reserve(2);
1509
1510                #[cfg(feature = "alloc")]
1511                buf.push(*modulation_frequency_id);
1512                #[cfg(not(feature = "alloc"))]
1513                buf.push(*modulation_frequency_id).unwrap();
1514
1515                #[cfg(feature = "alloc")]
1516                buf.push(*modulation_frequency_count);
1517                #[cfg(not(feature = "alloc"))]
1518                buf.push(*modulation_frequency_count).unwrap();
1519            }
1520            Self::GetModulationFrequencyDescription {
1521                modulation_frequency_id,
1522                frequency,
1523                description,
1524            } => {
1525                #[cfg(feature = "alloc")]
1526                buf.reserve(5 + description.len());
1527
1528                #[cfg(feature = "alloc")]
1529                buf.push(*modulation_frequency_id);
1530                #[cfg(not(feature = "alloc"))]
1531                buf.push(*modulation_frequency_id).unwrap();
1532
1533                buf.extend(frequency.to_be_bytes());
1534                buf.extend(description.bytes());
1535            }
1536            Self::GetPresetInfo {
1537                level_field_supported,
1538                preset_sequence_supported,
1539                split_times_supported,
1540                dmx_fail_infinite_delay_time_supported,
1541                dmx_fail_infinite_hold_time_supported,
1542                startup_infinite_hold_time_supported,
1543                maximum_scene_number,
1544                minimum_preset_fade_time_supported,
1545                maximum_preset_fade_time_supported,
1546                minimum_preset_wait_time_supported,
1547                maximum_preset_wait_time_supported,
1548                minimum_dmx_fail_delay_time_supported,
1549                maximum_dmx_fail_delay_time_supported,
1550                minimum_dmx_fail_hold_time_supported,
1551                maximum_dmx_fail_hold_time_supported,
1552                minimum_startup_delay_time_supported,
1553                maximum_startup_delay_time_supported,
1554                minimum_startup_hold_time_supported,
1555                maximum_startup_hold_time_supported,
1556            } => {
1557                #[cfg(feature = "alloc")]
1558                buf.reserve(38);
1559
1560                #[cfg(feature = "alloc")]
1561                buf.push(*level_field_supported as u8);
1562                #[cfg(not(feature = "alloc"))]
1563                buf.push(*level_field_supported as u8).unwrap();
1564
1565                #[cfg(feature = "alloc")]
1566                buf.push(*preset_sequence_supported as u8);
1567                #[cfg(not(feature = "alloc"))]
1568                buf.push(*preset_sequence_supported as u8).unwrap();
1569
1570                #[cfg(feature = "alloc")]
1571                buf.push(*split_times_supported as u8);
1572                #[cfg(not(feature = "alloc"))]
1573                buf.push(*split_times_supported as u8).unwrap();
1574
1575                #[cfg(feature = "alloc")]
1576                buf.push(*dmx_fail_infinite_delay_time_supported as u8);
1577                #[cfg(not(feature = "alloc"))]
1578                buf.push(*dmx_fail_infinite_delay_time_supported as u8)
1579                    .unwrap();
1580
1581                #[cfg(feature = "alloc")]
1582                buf.push(*dmx_fail_infinite_hold_time_supported as u8);
1583                #[cfg(not(feature = "alloc"))]
1584                buf.push(*dmx_fail_infinite_hold_time_supported as u8)
1585                    .unwrap();
1586
1587                #[cfg(feature = "alloc")]
1588                buf.push(*startup_infinite_hold_time_supported as u8);
1589                #[cfg(not(feature = "alloc"))]
1590                buf.push(*startup_infinite_hold_time_supported as u8)
1591                    .unwrap();
1592
1593                buf.extend(maximum_scene_number.to_be_bytes());
1594                buf.extend(minimum_preset_fade_time_supported.to_be_bytes());
1595                buf.extend(maximum_preset_fade_time_supported.to_be_bytes());
1596                buf.extend(minimum_preset_wait_time_supported.to_be_bytes());
1597                buf.extend(maximum_preset_wait_time_supported.to_be_bytes());
1598
1599                buf.extend(u16::from(*minimum_dmx_fail_delay_time_supported).to_be_bytes());
1600                buf.extend(u16::from(*maximum_dmx_fail_delay_time_supported).to_be_bytes());
1601                buf.extend(u16::from(*minimum_dmx_fail_hold_time_supported).to_be_bytes());
1602                buf.extend(u16::from(*maximum_dmx_fail_hold_time_supported).to_be_bytes());
1603                buf.extend(u16::from(*minimum_startup_delay_time_supported).to_be_bytes());
1604                buf.extend(u16::from(*maximum_startup_delay_time_supported).to_be_bytes());
1605                buf.extend(u16::from(*minimum_startup_hold_time_supported).to_be_bytes());
1606                buf.extend(u16::from(*maximum_startup_hold_time_supported).to_be_bytes());
1607            }
1608            Self::GetPresetStatus {
1609                scene_id,
1610                up_fade_time,
1611                down_fade_time,
1612                wait_time,
1613                programmed,
1614            } => {
1615                #[cfg(feature = "alloc")]
1616                buf.reserve(9);
1617
1618                buf.extend(scene_id.to_be_bytes());
1619                buf.extend(up_fade_time.to_be_bytes());
1620                buf.extend(down_fade_time.to_be_bytes());
1621                buf.extend(wait_time.to_be_bytes());
1622
1623                #[cfg(feature = "alloc")]
1624                buf.push(*programmed as u8);
1625                #[cfg(not(feature = "alloc"))]
1626                buf.push(*programmed as u8).unwrap();
1627            }
1628            Self::GetPresetMergeMode(mode) => {
1629                #[cfg(feature = "alloc")]
1630                buf.reserve(1);
1631
1632                #[cfg(feature = "alloc")]
1633                buf.push(*mode as u8);
1634                #[cfg(not(feature = "alloc"))]
1635                buf.push(*mode as u8).unwrap();
1636            }
1637            Self::GetListInterfaces(interfaces) => {
1638                #[cfg(feature = "alloc")]
1639                buf.reserve(interfaces.len() * 6);
1640
1641                for interface in interfaces {
1642                    buf.extend(interface.interface_id.to_be_bytes());
1643                    buf.extend(u16::from(interface.hardware_type).to_be_bytes());
1644                }
1645            }
1646            Self::GetInterfaceLabel {
1647                interface_id,
1648                interface_label,
1649            } => {
1650                #[cfg(feature = "alloc")]
1651                buf.reserve(4 + interface_label.len());
1652
1653                buf.extend(interface_id.to_be_bytes());
1654                buf.extend(interface_label.bytes());
1655            }
1656            Self::GetInterfaceHardwareAddressType1 {
1657                interface_id,
1658                hardware_address,
1659            } => {
1660                #[cfg(feature = "alloc")]
1661                buf.reserve(10);
1662
1663                buf.extend(interface_id.to_be_bytes());
1664                buf.extend(hardware_address.into_array());
1665            }
1666            Self::GetIpV4DhcpMode {
1667                interface_id,
1668                dhcp_mode,
1669            } => {
1670                #[cfg(feature = "alloc")]
1671                buf.reserve(5);
1672
1673                buf.extend(interface_id.to_be_bytes());
1674
1675                #[cfg(feature = "alloc")]
1676                buf.push(*dhcp_mode as u8);
1677                #[cfg(not(feature = "alloc"))]
1678                buf.push(*dhcp_mode as u8).unwrap();
1679            }
1680            Self::GetIpV4ZeroConfMode {
1681                interface_id,
1682                zero_conf_mode,
1683            } => {
1684                #[cfg(feature = "alloc")]
1685                buf.reserve(5);
1686
1687                buf.extend(interface_id.to_be_bytes());
1688
1689                #[cfg(feature = "alloc")]
1690                buf.push(*zero_conf_mode as u8);
1691                #[cfg(not(feature = "alloc"))]
1692                buf.push(*zero_conf_mode as u8).unwrap();
1693            }
1694            Self::GetIpV4CurrentAddress {
1695                interface_id,
1696                address,
1697                netmask,
1698                dhcp_status,
1699            } => {
1700                #[cfg(feature = "alloc")]
1701                buf.reserve(13);
1702
1703                buf.extend(interface_id.to_be_bytes());
1704                buf.extend(<[u8; 4]>::from(*address));
1705
1706                #[cfg(feature = "alloc")]
1707                buf.push(*netmask);
1708                #[cfg(not(feature = "alloc"))]
1709                buf.push(*netmask).unwrap();
1710
1711                #[cfg(feature = "alloc")]
1712                buf.push(*dhcp_status as u8);
1713                #[cfg(not(feature = "alloc"))]
1714                buf.push(*dhcp_status as u8).unwrap();
1715            }
1716            Self::GetIpV4StaticAddress {
1717                interface_id,
1718                address,
1719                netmask,
1720            } => {
1721                #[cfg(feature = "alloc")]
1722                buf.reserve(10);
1723
1724                buf.extend(interface_id.to_be_bytes());
1725                buf.extend(<[u8; 4]>::from(*address));
1726
1727                #[cfg(feature = "alloc")]
1728                buf.push(*netmask);
1729                #[cfg(not(feature = "alloc"))]
1730                buf.push(*netmask).unwrap();
1731            }
1732            Self::GetIpV4DefaultRoute {
1733                interface_id,
1734                address,
1735            } => {
1736                #[cfg(feature = "alloc")]
1737                buf.reserve(6);
1738
1739                buf.extend(interface_id.to_be_bytes());
1740                buf.extend(<[u8; 4]>::from(*address));
1741            }
1742            Self::GetDnsIpV4NameServer {
1743                name_server_index,
1744                address,
1745            } => {
1746                #[cfg(feature = "alloc")]
1747                buf.reserve(5);
1748
1749                buf.extend(name_server_index.to_be_bytes());
1750                buf.extend(<[u8; 4]>::from(*address));
1751            }
1752            Self::GetDnsHostName(host_name) => {
1753                #[cfg(feature = "alloc")]
1754                buf.reserve(host_name.len());
1755
1756                buf.extend(host_name.bytes());
1757            }
1758            Self::GetDnsDomainName(domain_name) => {
1759                #[cfg(feature = "alloc")]
1760                buf.reserve(domain_name.len());
1761
1762                buf.extend(domain_name.bytes());
1763            }
1764            // E1.37-7
1765            Self::GetEndpointList {
1766                list_change_number,
1767                endpoint_list,
1768            } => {
1769                #[cfg(feature = "alloc")]
1770                buf.reserve(4 + (endpoint_list.len() * 3));
1771
1772                buf.extend(list_change_number.to_be_bytes());
1773
1774                for (endpoint_id, endpoint_type) in endpoint_list {
1775                    buf.extend(u16::from(*endpoint_id).to_be_bytes());
1776
1777                    #[cfg(feature = "alloc")]
1778                    buf.push(*endpoint_type as u8);
1779                    #[cfg(not(feature = "alloc"))]
1780                    buf.push(*endpoint_type as u8).unwrap();
1781                }
1782            }
1783            Self::GetEndpointListChange { list_change_number } => {
1784                #[cfg(feature = "alloc")]
1785                buf.reserve(4);
1786
1787                buf.extend(list_change_number.to_be_bytes());
1788            }
1789            Self::GetIdentifyEndpoint {
1790                endpoint_id,
1791                identify,
1792            } => {
1793                #[cfg(feature = "alloc")]
1794                buf.reserve(3);
1795
1796                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1797
1798                #[cfg(feature = "alloc")]
1799                buf.push(*identify as u8);
1800                #[cfg(not(feature = "alloc"))]
1801                buf.push(*identify as u8).unwrap();
1802            }
1803            Self::SetIdentifyEndpoint { endpoint_id } => {
1804                #[cfg(feature = "alloc")]
1805                buf.reserve(2);
1806
1807                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1808            }
1809            Self::GetEndpointToUniverse {
1810                endpoint_id,
1811                universe,
1812            } => {
1813                #[cfg(feature = "alloc")]
1814                buf.reserve(4);
1815
1816                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1817                buf.extend(universe.to_be_bytes());
1818            }
1819            Self::SetEndpointToUniverse { endpoint_id } => {
1820                #[cfg(feature = "alloc")]
1821                buf.reserve(2);
1822
1823                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1824            }
1825            Self::GetEndpointMode { endpoint_id, mode } => {
1826                #[cfg(feature = "alloc")]
1827                buf.reserve(3);
1828
1829                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1830
1831                #[cfg(feature = "alloc")]
1832                buf.push(*mode as u8);
1833                #[cfg(not(feature = "alloc"))]
1834                buf.push(*mode as u8).unwrap();
1835            }
1836            Self::SetEndpointMode { endpoint_id } => {
1837                #[cfg(feature = "alloc")]
1838                buf.reserve(2);
1839
1840                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1841            }
1842            Self::GetEndpointLabel { endpoint_id, label } => {
1843                #[cfg(feature = "alloc")]
1844                buf.reserve(3 + label.len());
1845
1846                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1847                buf.extend(label.bytes());
1848            }
1849            Self::SetEndpointLabel { endpoint_id } => {
1850                #[cfg(feature = "alloc")]
1851                buf.reserve(2);
1852
1853                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1854            }
1855            Self::GetRdmTrafficEnable {
1856                endpoint_id,
1857                enable,
1858            } => {
1859                #[cfg(feature = "alloc")]
1860                buf.reserve(3);
1861
1862                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1863
1864                #[cfg(feature = "alloc")]
1865                buf.push(*enable as u8);
1866                #[cfg(not(feature = "alloc"))]
1867                buf.push(*enable as u8).unwrap();
1868            }
1869            Self::SetRdmTrafficEnable { endpoint_id } => {
1870                #[cfg(feature = "alloc")]
1871                buf.reserve(2);
1872
1873                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1874            }
1875            Self::GetDiscoveryState {
1876                endpoint_id,
1877                device_count,
1878                discovery_state,
1879            } => {
1880                #[cfg(feature = "alloc")]
1881                buf.reserve(5);
1882
1883                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1884                buf.extend(u16::from(*device_count).to_be_bytes());
1885
1886                #[cfg(feature = "alloc")]
1887                buf.push((*discovery_state).into());
1888                #[cfg(not(feature = "alloc"))]
1889                buf.push((*discovery_state).into()).unwrap();
1890            }
1891            Self::SetDiscoveryState { endpoint_id } => {
1892                #[cfg(feature = "alloc")]
1893                buf.reserve(2);
1894
1895                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1896            }
1897            Self::GetBackgroundDiscovery {
1898                endpoint_id,
1899                enabled,
1900            } => {
1901                #[cfg(feature = "alloc")]
1902                buf.reserve(3);
1903
1904                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1905
1906                #[cfg(feature = "alloc")]
1907                buf.push(*enabled as u8);
1908                #[cfg(not(feature = "alloc"))]
1909                buf.push(*enabled as u8).unwrap();
1910            }
1911            Self::SetBackgroundDiscovery { endpoint_id } => {
1912                #[cfg(feature = "alloc")]
1913                buf.reserve(2);
1914
1915                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1916            }
1917            Self::GetEndpointTiming {
1918                endpoint_id,
1919                current_setting_id,
1920                setting_count,
1921            } => {
1922                #[cfg(feature = "alloc")]
1923                buf.reserve(4);
1924
1925                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1926
1927                #[cfg(feature = "alloc")]
1928                buf.push(*current_setting_id);
1929                #[cfg(not(feature = "alloc"))]
1930                buf.push(*current_setting_id).unwrap();
1931
1932                #[cfg(feature = "alloc")]
1933                buf.push(*setting_count);
1934                #[cfg(not(feature = "alloc"))]
1935                buf.push(*setting_count).unwrap();
1936            }
1937            Self::SetEndpointTiming { endpoint_id } => {
1938                #[cfg(feature = "alloc")]
1939                buf.reserve(2);
1940
1941                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1942            }
1943            Self::GetEndpointTimingDescription {
1944                setting_id,
1945                description,
1946            } => {
1947                #[cfg(feature = "alloc")]
1948                buf.reserve(1 + description.len());
1949
1950                #[cfg(feature = "alloc")]
1951                buf.push(*setting_id);
1952                #[cfg(not(feature = "alloc"))]
1953                buf.push(*setting_id).unwrap();
1954
1955                buf.extend(description.bytes());
1956            }
1957            Self::GetEndpointResponders {
1958                endpoint_id,
1959                list_change_number,
1960                responders,
1961            } => {
1962                #[cfg(feature = "alloc")]
1963                buf.reserve(6 + (responders.len() * 6));
1964
1965                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1966                buf.extend(list_change_number.to_be_bytes());
1967
1968                for responder in responders {
1969                    buf.extend(<[u8; 6]>::from(*responder));
1970                }
1971            }
1972            Self::GetEndpointResponderListChange {
1973                endpoint_id,
1974                list_change_number,
1975            } => {
1976                #[cfg(feature = "alloc")]
1977                buf.reserve(6);
1978
1979                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1980                buf.extend(list_change_number.to_be_bytes());
1981            }
1982            Self::GetBindingControlFields {
1983                endpoint_id,
1984                uid,
1985                control_field,
1986                binding_uid,
1987            } => {
1988                #[cfg(feature = "alloc")]
1989                buf.reserve(3);
1990
1991                buf.extend(u16::from(*endpoint_id).to_be_bytes());
1992                buf.extend(<[u8; 6]>::from(*uid));
1993                buf.extend((*control_field).to_be_bytes());
1994                buf.extend(<[u8; 6]>::from(*binding_uid));
1995            }
1996            Self::GetBackgroundQueuedStatusPolicy {
1997                current_policy_id,
1998                policy_count,
1999            } => {
2000                #[cfg(feature = "alloc")]
2001                buf.reserve(2);
2002
2003                #[cfg(feature = "alloc")]
2004                buf.push(*current_policy_id);
2005                #[cfg(not(feature = "alloc"))]
2006                buf.push(*current_policy_id).unwrap();
2007
2008                #[cfg(feature = "alloc")]
2009                buf.push(*policy_count);
2010                #[cfg(not(feature = "alloc"))]
2011                buf.push(*policy_count).unwrap();
2012            }
2013            Self::GetBackgroundQueuedStatusPolicyDescription {
2014                policy_id,
2015                description,
2016            } => {
2017                #[cfg(feature = "alloc")]
2018                buf.reserve(1 + description.len());
2019
2020                #[cfg(feature = "alloc")]
2021                buf.push(*policy_id);
2022                #[cfg(not(feature = "alloc"))]
2023                buf.push(*policy_id).unwrap();
2024
2025                buf.extend(description.bytes());
2026            }
2027            // E1.33
2028            Self::GetComponentScope {
2029                scope_slot,
2030                scope_string,
2031                static_config_type,
2032                static_ipv4_address,
2033                static_ipv6_address,
2034                static_port,
2035            } => {
2036                #[cfg(feature = "alloc")]
2037                buf.reserve(25 + scope_string.len());
2038
2039                buf.extend(scope_slot.to_be_bytes());
2040                buf.extend(scope_string.bytes());
2041
2042                #[cfg(feature = "alloc")]
2043                buf.push(*static_config_type as u8);
2044                #[cfg(not(feature = "alloc"))]
2045                buf.push(*static_config_type as u8).unwrap();
2046
2047                buf.extend(<[u8; 4]>::from(*static_ipv4_address));
2048                buf.extend(<[u8; 16]>::from(*static_ipv6_address));
2049                buf.extend(static_port.to_be_bytes());
2050            }
2051            Self::GetSearchDomain(search_domain) => {
2052                #[cfg(feature = "alloc")]
2053                buf.reserve(25 + search_domain.len());
2054
2055                buf.extend(search_domain.bytes());
2056            }
2057            Self::GetTcpCommsStatus {
2058                scope_string,
2059                broker_ipv4_address,
2060                broker_ipv6_address,
2061                broker_port,
2062                unhealthy_tcp_events,
2063            } => {
2064                #[cfg(feature = "alloc")]
2065                buf.reserve(24 + scope_string.len());
2066
2067                buf.extend(scope_string.bytes());
2068
2069                buf.extend(<[u8; 4]>::from(*broker_ipv4_address));
2070                buf.extend(<[u8; 16]>::from(*broker_ipv6_address));
2071                buf.extend(broker_port.to_be_bytes());
2072                buf.extend(unhealthy_tcp_events.to_be_bytes());
2073            }
2074            Self::GetBrokerStatus {
2075                is_allowing_set_commands,
2076                broker_state,
2077            } => {
2078                #[cfg(feature = "alloc")]
2079                buf.reserve(2);
2080
2081                #[cfg(feature = "alloc")]
2082                buf.push(*is_allowing_set_commands as u8);
2083                #[cfg(not(feature = "alloc"))]
2084                buf.push(*is_allowing_set_commands as u8).unwrap();
2085
2086                #[cfg(feature = "alloc")]
2087                buf.push(*broker_state as u8);
2088                #[cfg(not(feature = "alloc"))]
2089                buf.push(*broker_state as u8).unwrap();
2090            }
2091            Self::ManufacturerSpecific(data) => {
2092                #[cfg(feature = "alloc")]
2093                buf.reserve(data.len());
2094
2095                #[cfg(feature = "alloc")]
2096                buf.extend(data);
2097                #[cfg(not(feature = "alloc"))]
2098                buf.extend_from_slice(data).unwrap();
2099            }
2100            Self::Unsupported(data) => {
2101                #[cfg(feature = "alloc")]
2102                buf.reserve(data.len());
2103
2104                #[cfg(feature = "alloc")]
2105                buf.extend(data);
2106                #[cfg(not(feature = "alloc"))]
2107                buf.extend_from_slice(data).unwrap();
2108            }
2109        }
2110
2111        buf
2112    }
2113
2114    pub fn decode(
2115        command_class: CommandClass,
2116        parameter_id: ParameterId,
2117        bytes: &[u8],
2118    ) -> Result<Self, RdmError> {
2119        match (command_class, parameter_id) {
2120            (CommandClass::DiscoveryCommandResponse, ParameterId::DiscMute) => {
2121                let binding_uid = if bytes.len() > 2 {
2122                    Some(DeviceUID::from(<[u8; 6]>::try_from(&bytes[2..=7])?))
2123                } else {
2124                    None
2125                };
2126
2127                Ok(Self::DiscMute {
2128                    control_field: u16::from_be_bytes(bytes[..=1].try_into()?),
2129                    binding_uid,
2130                })
2131            }
2132            (CommandClass::DiscoveryCommandResponse, ParameterId::DiscUnMute) => {
2133                let binding_uid = if bytes.len() > 2 {
2134                    Some(DeviceUID::from(<[u8; 6]>::try_from(&bytes[2..=7])?))
2135                } else {
2136                    None
2137                };
2138
2139                Ok(Self::DiscUnMute {
2140                    control_field: u16::from_be_bytes(bytes[..=1].try_into()?),
2141                    binding_uid,
2142                })
2143            }
2144            (CommandClass::GetCommandResponse, ParameterId::ProxiedDeviceCount) => {
2145                Ok(Self::GetProxiedDeviceCount {
2146                    device_count: u16::from_be_bytes(bytes[0..=1].try_into()?),
2147                    list_change: bytes[2] == 1,
2148                })
2149            }
2150            (CommandClass::GetCommandResponse, ParameterId::ProxiedDevices) => {
2151                Ok(Self::GetProxiedDevices(
2152                    #[cfg(feature = "alloc")]
2153                    bytes
2154                        .chunks(6)
2155                        .map(|chunk| Ok(DeviceUID::from(<[u8; 6]>::try_from(&chunk[0..=5])?)))
2156                        .collect::<Result<Vec<DeviceUID>, RdmError>>()?,
2157                    #[cfg(not(feature = "alloc"))]
2158                    bytes
2159                        .chunks(6)
2160                        .map(|chunk| Ok(DeviceUID::from(<[u8; 6]>::try_from(&chunk[0..=5])?)))
2161                        .collect::<Result<Vec<DeviceUID, 38>, RdmError>>()?,
2162                ))
2163            }
2164            (CommandClass::GetCommandResponse, ParameterId::CommsStatus) => {
2165                Ok(Self::GetCommsStatus {
2166                    short_message: u16::from_be_bytes(bytes[0..=1].try_into()?),
2167                    length_mismatch: u16::from_be_bytes(bytes[2..=3].try_into()?),
2168                    checksum_fail: u16::from_be_bytes(bytes[4..=5].try_into()?),
2169                })
2170            }
2171            (CommandClass::GetCommandResponse, ParameterId::StatusMessages) => {
2172                Ok(Self::GetStatusMessages(
2173                    #[cfg(feature = "alloc")]
2174                    bytes
2175                        .chunks(9)
2176                        .map(|chunk| {
2177                            Ok(StatusMessage::new(
2178                                u16::from_be_bytes(chunk[0..=1].try_into()?).into(),
2179                                chunk[2].try_into()?,
2180                                u16::from_be_bytes(chunk[3..=4].try_into()?),
2181                                u16::from_be_bytes(chunk[5..=6].try_into()?),
2182                                u16::from_be_bytes(chunk[7..=8].try_into()?),
2183                            ))
2184                        })
2185                        .collect::<Result<Vec<StatusMessage>, RdmError>>()?,
2186                    #[cfg(not(feature = "alloc"))]
2187                    bytes
2188                        .chunks(9)
2189                        .map(|chunk| {
2190                            Ok(StatusMessage::new(
2191                                u16::from_be_bytes(chunk[0..=1].try_into()?).into(),
2192                                chunk[2].try_into()?,
2193                                u16::from_be_bytes(chunk[3..=4].try_into()?),
2194                                u16::from_be_bytes(chunk[5..=6].try_into()?),
2195                                u16::from_be_bytes(chunk[7..=8].try_into()?),
2196                            ))
2197                        })
2198                        .collect::<Result<Vec<StatusMessage, 25>, RdmError>>()?,
2199                ))
2200            }
2201            (CommandClass::GetCommandResponse, ParameterId::StatusIdDescription) => {
2202                Ok(Self::GetStatusIdDescription(decode_string_bytes(bytes)?))
2203            }
2204            (CommandClass::GetCommandResponse, ParameterId::SubDeviceIdStatusReportThreshold) => {
2205                Ok(Self::GetSubDeviceIdStatusReportThreshold(
2206                    bytes[0].try_into()?,
2207                ))
2208            }
2209            (CommandClass::GetCommandResponse, ParameterId::SupportedParameters) => {
2210                let parameters = bytes
2211                    .chunks(2)
2212                    .map(|chunk| Ok(u16::from_be_bytes(chunk.try_into()?)))
2213                    .filter_map(|parameter_id: Result<u16, RdmError>| parameter_id.ok());
2214
2215                Ok(Self::GetSupportedParameters(
2216                    #[cfg(feature = "alloc")]
2217                    parameters.collect::<Vec<u16>>(),
2218                    #[cfg(not(feature = "alloc"))]
2219                    parameters.collect::<Vec<u16, 115>>(),
2220                ))
2221            }
2222            (CommandClass::GetCommandResponse, ParameterId::ParameterDescription) => {
2223                Ok(Self::GetParameterDescription(ParameterDescription {
2224                    parameter_id: u16::from_be_bytes(bytes[0..=1].try_into()?),
2225                    parameter_data_length: bytes[2],
2226                    data_type: bytes[3].try_into()?,
2227                    command_class: bytes[4].try_into()?,
2228                    unit_type: bytes[6].try_into()?,
2229                    prefix: bytes[7].try_into()?,
2230                    raw_minimum_valid_value: bytes[8..=11].try_into()?,
2231                    raw_maximum_valid_value: bytes[12..=15].try_into()?,
2232                    raw_default_value: bytes[16..=19].try_into()?,
2233                    description: decode_string_bytes(&bytes[20..])?,
2234                }))
2235            }
2236            (CommandClass::GetCommandResponse, ParameterId::DeviceInfo) => {
2237                Ok(Self::GetDeviceInfo {
2238                    protocol_version: ProtocolVersion::new(bytes[0], bytes[1]),
2239                    model_id: u16::from_be_bytes(bytes[2..=3].try_into()?),
2240                    product_category: u16::from_be_bytes(bytes[4..=5].try_into()?).into(),
2241                    software_version_id: u32::from_be_bytes(bytes[6..=9].try_into()?),
2242                    footprint: u16::from_be_bytes(bytes[10..=11].try_into()?),
2243                    current_personality: bytes[12],
2244                    personality_count: bytes[13],
2245                    start_address: u16::from_be_bytes(bytes[14..=15].try_into()?),
2246                    sub_device_count: u16::from_be_bytes(bytes[16..=17].try_into()?),
2247                    sensor_count: u8::from_be(bytes[18]),
2248                })
2249            }
2250            (CommandClass::GetCommandResponse, ParameterId::ProductDetailIdList) => {
2251                Ok(Self::GetProductDetailIdList(
2252                    #[cfg(feature = "alloc")]
2253                    bytes
2254                        .chunks(2)
2255                        .map(|chunk| Ok(u16::from_be_bytes(chunk.try_into()?).into()))
2256                        .collect::<Result<Vec<ProductDetail>, RdmError>>()?,
2257                    #[cfg(not(feature = "alloc"))]
2258                    bytes
2259                        .chunks(2)
2260                        .map(|chunk| Ok(u16::from_be_bytes(chunk.try_into()?).into()))
2261                        .collect::<Result<Vec<ProductDetail, 115>, RdmError>>()?,
2262                ))
2263            }
2264            (CommandClass::GetCommandResponse, ParameterId::DeviceModelDescription) => {
2265                Ok(Self::GetDeviceModelDescription(decode_string_bytes(bytes)?))
2266            }
2267            (CommandClass::GetCommandResponse, ParameterId::ManufacturerLabel) => {
2268                Ok(Self::GetManufacturerLabel(decode_string_bytes(bytes)?))
2269            }
2270            (CommandClass::GetCommandResponse, ParameterId::DeviceLabel) => {
2271                Ok(Self::GetDeviceLabel(decode_string_bytes(bytes)?))
2272            }
2273            (CommandClass::GetCommandResponse, ParameterId::FactoryDefaults) => {
2274                Ok(Self::GetFactoryDefaults(bytes[0] == 1))
2275            }
2276            (CommandClass::GetCommandResponse, ParameterId::LanguageCapabilities) => {
2277                Ok(Self::GetLanguageCapabilities(
2278                    #[cfg(feature = "alloc")]
2279                    bytes
2280                        .chunks(2)
2281                        .map(|chunk| Ok(core::str::from_utf8(chunk)?.to_string()))
2282                        .collect::<Result<Vec<String>, RdmError>>()?,
2283                    #[cfg(not(feature = "alloc"))]
2284                    bytes
2285                        .chunks(2)
2286                        .map(|chunk| {
2287                            Ok(String::from_utf8(Vec::<u8, 2>::from_slice(chunk).unwrap())?)
2288                        })
2289                        .collect::<Result<Vec<String<2>, 115>, RdmError>>()?,
2290                ))
2291            }
2292            (CommandClass::GetCommandResponse, ParameterId::Language) => Ok(Self::GetLanguage(
2293                #[cfg(feature = "alloc")]
2294                core::str::from_utf8(&bytes[0..=1])?.to_string(),
2295                #[cfg(not(feature = "alloc"))]
2296                String::from_utf8(Vec::<u8, 2>::from_slice(&bytes[0..=1]).unwrap())?,
2297            )),
2298            (CommandClass::GetCommandResponse, ParameterId::SoftwareVersionLabel) => {
2299                Ok(Self::GetSoftwareVersionLabel(decode_string_bytes(bytes)?))
2300            }
2301            (CommandClass::GetCommandResponse, ParameterId::BootSoftwareVersionId) => Ok(
2302                Self::GetBootSoftwareVersionId(u32::from_be_bytes(bytes.try_into()?)),
2303            ),
2304            (CommandClass::GetCommandResponse, ParameterId::BootSoftwareVersionLabel) => Ok(
2305                Self::GetBootSoftwareVersionLabel(decode_string_bytes(bytes)?),
2306            ),
2307            (CommandClass::GetCommandResponse, ParameterId::DmxPersonality) => {
2308                Ok(Self::GetDmxPersonality {
2309                    current_personality: bytes[0],
2310                    personality_count: bytes[1],
2311                })
2312            }
2313            (CommandClass::GetCommandResponse, ParameterId::DmxPersonalityDescription) => {
2314                Ok(Self::GetDmxPersonalityDescription {
2315                    id: bytes[0],
2316                    dmx_slots_required: u16::from_be_bytes(bytes[1..=2].try_into()?),
2317                    description: decode_string_bytes(&bytes[3..])?,
2318                })
2319            }
2320            (CommandClass::GetCommandResponse, ParameterId::DmxStartAddress) => Ok(
2321                Self::GetDmxStartAddress(u16::from_be_bytes(bytes[0..=1].try_into()?)),
2322            ),
2323            (CommandClass::GetCommandResponse, ParameterId::SlotInfo) => Ok(Self::GetSlotInfo(
2324                #[cfg(feature = "alloc")]
2325                bytes
2326                    .chunks(5)
2327                    .map(|chunk| {
2328                        Ok(SlotInfo::new(
2329                            u16::from_be_bytes(chunk[0..=1].try_into()?),
2330                            chunk[2].into(),
2331                            u16::from_be_bytes(chunk[3..=4].try_into()?),
2332                        ))
2333                    })
2334                    .collect::<Result<Vec<SlotInfo>, RdmError>>()?,
2335                #[cfg(not(feature = "alloc"))]
2336                bytes
2337                    .chunks(5)
2338                    .map(|chunk| {
2339                        Ok(SlotInfo::new(
2340                            u16::from_be_bytes(chunk[0..=1].try_into()?),
2341                            chunk[2].into(),
2342                            u16::from_be_bytes(chunk[3..=4].try_into()?),
2343                        ))
2344                    })
2345                    .collect::<Result<Vec<SlotInfo, 46>, RdmError>>()?,
2346            )),
2347            (CommandClass::GetCommandResponse, ParameterId::SlotDescription) => {
2348                Ok(Self::GetSlotDescription {
2349                    slot_id: u16::from_be_bytes(bytes[0..=1].try_into()?),
2350                    description: decode_string_bytes(&bytes[2..])?,
2351                })
2352            }
2353            (CommandClass::GetCommandResponse, ParameterId::DefaultSlotValue) => {
2354                Ok(Self::GetDefaultSlotValue(
2355                    #[cfg(feature = "alloc")]
2356                    bytes
2357                        .chunks(3)
2358                        .map(|chunk| {
2359                            Ok(DefaultSlotValue::new(
2360                                u16::from_be_bytes(chunk[0..=1].try_into()?),
2361                                chunk[2],
2362                            ))
2363                        })
2364                        .collect::<Result<Vec<DefaultSlotValue>, RdmError>>()?,
2365                    #[cfg(not(feature = "alloc"))]
2366                    bytes
2367                        .chunks(3)
2368                        .map(|chunk| {
2369                            Ok(DefaultSlotValue::new(
2370                                u16::from_be_bytes(chunk[0..=1].try_into()?),
2371                                chunk[2],
2372                            ))
2373                        })
2374                        .collect::<Result<Vec<DefaultSlotValue, 77>, RdmError>>()?,
2375                ))
2376            }
2377            (CommandClass::GetCommandResponse, ParameterId::SensorDefinition) => {
2378                Ok(Self::GetSensorDefinition(SensorDefinition {
2379                    id: bytes[0],
2380                    kind: bytes[1].try_into()?,
2381                    unit: bytes[2].try_into()?,
2382                    prefix: bytes[3].try_into()?,
2383                    range_minimum_value: i16::from_be_bytes(bytes[4..=5].try_into()?),
2384                    range_maximum_value: i16::from_be_bytes(bytes[6..=7].try_into()?),
2385                    normal_minimum_value: i16::from_be_bytes(bytes[8..=9].try_into()?),
2386                    normal_maximum_value: i16::from_be_bytes(bytes[10..=11].try_into()?),
2387                    is_lowest_highest_detected_value_supported: bytes[12] >> 1 & 1 == 1,
2388                    is_recorded_value_supported: bytes[12] & 1 == 1,
2389                    description: decode_string_bytes(&bytes[13..])?,
2390                }))
2391            }
2392            (CommandClass::GetCommandResponse, ParameterId::SensorValue) => {
2393                Ok(Self::GetSensorValue(SensorValue::new(
2394                    bytes[0],
2395                    i16::from_be_bytes(bytes[1..=2].try_into()?),
2396                    i16::from_be_bytes(bytes[3..=4].try_into()?),
2397                    i16::from_be_bytes(bytes[5..=6].try_into()?),
2398                    i16::from_be_bytes(bytes[7..=8].try_into()?),
2399                )))
2400            }
2401            (CommandClass::SetCommandResponse, ParameterId::SensorValue) => {
2402                Ok(Self::SetSensorValue(SensorValue::new(
2403                    bytes[0],
2404                    i16::from_be_bytes(bytes[1..=2].try_into()?),
2405                    i16::from_be_bytes(bytes[3..=4].try_into()?),
2406                    i16::from_be_bytes(bytes[5..=6].try_into()?),
2407                    i16::from_be_bytes(bytes[7..=8].try_into()?),
2408                )))
2409            }
2410            (CommandClass::GetCommandResponse, ParameterId::DeviceHours) => Ok(
2411                Self::GetDeviceHours(u32::from_be_bytes(bytes[0..=3].try_into()?)),
2412            ),
2413            (CommandClass::GetCommandResponse, ParameterId::LampHours) => Ok(Self::GetLampHours(
2414                u32::from_be_bytes(bytes[0..=3].try_into()?),
2415            )),
2416            (CommandClass::GetCommandResponse, ParameterId::LampStrikes) => Ok(
2417                Self::GetLampStrikes(u32::from_be_bytes(bytes[0..=3].try_into()?)),
2418            ),
2419            (CommandClass::GetCommandResponse, ParameterId::LampState) => {
2420                Ok(Self::GetLampState(bytes[0].try_into()?))
2421            }
2422            (CommandClass::GetCommandResponse, ParameterId::LampOnMode) => {
2423                Ok(Self::GetLampOnMode(bytes[0].try_into()?))
2424            }
2425            (CommandClass::GetCommandResponse, ParameterId::DevicePowerCycles) => Ok(
2426                Self::GetDevicePowerCycles(u32::from_be_bytes(bytes[0..=3].try_into()?)),
2427            ),
2428            (CommandClass::GetCommandResponse, ParameterId::DisplayInvert) => {
2429                Ok(Self::GetDisplayInvert(bytes[0].try_into()?))
2430            }
2431            (CommandClass::GetCommandResponse, ParameterId::DisplayLevel) => {
2432                Ok(Self::GetDisplayLevel(bytes[0]))
2433            }
2434            (CommandClass::GetCommandResponse, ParameterId::PanInvert) => {
2435                Ok(Self::GetPanInvert(bytes[0] == 1))
2436            }
2437            (CommandClass::GetCommandResponse, ParameterId::TiltInvert) => {
2438                Ok(Self::GetTiltInvert(bytes[0] == 1))
2439            }
2440            (CommandClass::GetCommandResponse, ParameterId::PanTiltSwap) => {
2441                Ok(Self::GetPanTiltSwap(bytes[0] == 1))
2442            }
2443            (CommandClass::GetCommandResponse, ParameterId::RealTimeClock) => {
2444                Ok(Self::GetRealTimeClock {
2445                    year: u16::from_be_bytes(bytes[0..=1].try_into()?),
2446                    month: bytes[2],
2447                    day: bytes[3],
2448                    hour: bytes[4],
2449                    minute: bytes[5],
2450                    second: bytes[6],
2451                })
2452            }
2453            (CommandClass::GetCommandResponse, ParameterId::IdentifyDevice) => {
2454                Ok(Self::GetIdentifyDevice(bytes[0] == 1))
2455            }
2456            (CommandClass::GetCommandResponse, ParameterId::PowerState) => {
2457                Ok(Self::GetPowerState(bytes[0].try_into()?))
2458            }
2459            (CommandClass::GetCommandResponse, ParameterId::PerformSelfTest) => {
2460                Ok(Self::GetPerformSelfTest(bytes[0] == 1))
2461            }
2462            (CommandClass::GetCommandResponse, ParameterId::SelfTestDescription) => {
2463                Ok(Self::GetSelfTestDescription {
2464                    self_test_id: bytes[0].into(),
2465                    description: decode_string_bytes(&bytes[1..])?,
2466                })
2467            }
2468            (CommandClass::GetCommandResponse, ParameterId::PresetPlayback) => {
2469                Ok(Self::GetPresetPlayback {
2470                    mode: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2471                    level: bytes[2],
2472                })
2473            }
2474            // E1.37-1
2475            (CommandClass::GetCommandResponse, ParameterId::DmxBlockAddress) => {
2476                Ok(Self::GetDmxBlockAddress {
2477                    total_sub_device_footprint: u16::from_be_bytes(bytes[0..=1].try_into()?),
2478                    base_dmx_address: u16::from_be_bytes(bytes[2..=3].try_into()?),
2479                })
2480            }
2481            (CommandClass::GetCommandResponse, ParameterId::DmxFailMode) => {
2482                Ok(Self::GetDmxFailMode {
2483                    scene_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2484                    loss_of_signal_delay: u16::from_be_bytes(bytes[2..=3].try_into()?).into(),
2485                    hold_time: u16::from_be_bytes(bytes[4..=5].try_into()?).into(),
2486                    level: bytes[6],
2487                })
2488            }
2489            (CommandClass::GetCommandResponse, ParameterId::DmxStartupMode) => {
2490                Ok(Self::GetDmxStartupMode {
2491                    scene_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2492                    startup_delay: u16::from_be_bytes(bytes[2..=3].try_into()?).into(),
2493                    hold_time: u16::from_be_bytes(bytes[4..=5].try_into()?).into(),
2494                    level: bytes[6],
2495                })
2496            }
2497            (CommandClass::GetCommandResponse, ParameterId::PowerOnSelfTest) => {
2498                Ok(Self::GetPowerOnSelfTest(bytes[0] == 1))
2499            }
2500            (CommandClass::GetCommandResponse, ParameterId::LockState) => Ok(Self::GetLockState {
2501                lock_state_id: bytes[0],
2502                lock_state_count: bytes[1],
2503            }),
2504            (CommandClass::GetCommandResponse, ParameterId::LockStateDescription) => {
2505                Ok(Self::GetLockStateDescription {
2506                    lock_state_id: bytes[0],
2507                    description: decode_string_bytes(&bytes[1..])?,
2508                })
2509            }
2510            (CommandClass::GetCommandResponse, ParameterId::LockPin) => Ok(Self::GetLockPin(
2511                PinCode::try_from(u16::from_be_bytes(bytes[0..=1].try_into()?))?,
2512            )),
2513            (CommandClass::GetCommandResponse, ParameterId::BurnIn) => {
2514                Ok(Self::GetBurnIn(bytes[0]))
2515            }
2516            (CommandClass::GetCommandResponse, ParameterId::DimmerInfo) => {
2517                Ok(Self::GetDimmerInfo {
2518                    minimum_level_lower_limit: u16::from_be_bytes(bytes[0..=1].try_into()?),
2519                    minimum_level_upper_limit: u16::from_be_bytes(bytes[2..=3].try_into()?),
2520                    maximum_level_lower_limit: u16::from_be_bytes(bytes[4..=5].try_into()?),
2521                    maximum_level_upper_limit: u16::from_be_bytes(bytes[6..=7].try_into()?),
2522                    number_of_supported_curves: bytes[8],
2523                    levels_resolution: bytes[9],
2524                    minimum_level_split_levels_supported: bytes[10] == 1,
2525                })
2526            }
2527            (CommandClass::GetCommandResponse, ParameterId::MinimumLevel) => {
2528                Ok(Self::GetMinimumLevel {
2529                    minimum_level_increasing: u16::from_be_bytes(bytes[0..=1].try_into()?),
2530                    minimum_level_decreasing: u16::from_be_bytes(bytes[2..=3].try_into()?),
2531                    on_below_minimum: bytes[4] == 1,
2532                })
2533            }
2534            (CommandClass::GetCommandResponse, ParameterId::MaximumLevel) => Ok(
2535                Self::GetMaximumLevel(u16::from_be_bytes(bytes[0..=1].try_into()?)),
2536            ),
2537            (CommandClass::GetCommandResponse, ParameterId::Curve) => Ok(Self::GetCurve {
2538                curve_id: bytes[0],
2539                curve_count: bytes[1],
2540            }),
2541            (CommandClass::GetCommandResponse, ParameterId::CurveDescription) => {
2542                Ok(Self::GetCurveDescription {
2543                    curve_id: bytes[0],
2544                    description: decode_string_bytes(&bytes[1..])?,
2545                })
2546            }
2547            (CommandClass::GetCommandResponse, ParameterId::OutputResponseTime) => {
2548                Ok(Self::GetOutputResponseTime {
2549                    response_time_id: bytes[0],
2550                    response_time_count: bytes[1],
2551                })
2552            }
2553            (CommandClass::GetCommandResponse, ParameterId::OutputResponseTimeDescription) => {
2554                Ok(Self::GetOutputResponseTimeDescription {
2555                    response_time_id: bytes[0],
2556                    description: decode_string_bytes(&bytes[1..])?,
2557                })
2558            }
2559            (CommandClass::GetCommandResponse, ParameterId::ModulationFrequency) => {
2560                Ok(Self::GetModulationFrequency {
2561                    modulation_frequency_id: bytes[0],
2562                    modulation_frequency_count: bytes[1],
2563                })
2564            }
2565            (CommandClass::GetCommandResponse, ParameterId::ModulationFrequencyDescription) => {
2566                Ok(Self::GetModulationFrequencyDescription {
2567                    modulation_frequency_id: bytes[0],
2568                    frequency: u32::from_be_bytes(bytes[1..=4].try_into()?),
2569                    description: decode_string_bytes(&bytes[5..])?,
2570                })
2571            }
2572            (CommandClass::GetCommandResponse, ParameterId::PresetInfo) => {
2573                Ok(Self::GetPresetInfo {
2574                    level_field_supported: bytes[0] == 1,
2575                    preset_sequence_supported: bytes[1] == 1,
2576                    split_times_supported: bytes[2] == 1,
2577                    dmx_fail_infinite_delay_time_supported: bytes[3] == 1,
2578                    dmx_fail_infinite_hold_time_supported: bytes[4] == 1,
2579                    startup_infinite_hold_time_supported: bytes[5] == 1,
2580                    maximum_scene_number: u16::from_be_bytes(bytes[6..=7].try_into()?),
2581                    minimum_preset_fade_time_supported: u16::from_be_bytes(
2582                        bytes[8..=9].try_into()?,
2583                    ),
2584                    maximum_preset_fade_time_supported: u16::from_be_bytes(
2585                        bytes[10..=11].try_into()?,
2586                    ),
2587                    minimum_preset_wait_time_supported: u16::from_be_bytes(
2588                        bytes[12..=13].try_into()?,
2589                    ),
2590                    maximum_preset_wait_time_supported: u16::from_be_bytes(
2591                        bytes[14..=15].try_into()?,
2592                    ),
2593                    minimum_dmx_fail_delay_time_supported: SupportedTimes::from(
2594                        u16::from_be_bytes(bytes[16..=17].try_into()?),
2595                    ),
2596                    maximum_dmx_fail_delay_time_supported: SupportedTimes::from(
2597                        u16::from_be_bytes(bytes[18..=19].try_into()?),
2598                    ),
2599                    minimum_dmx_fail_hold_time_supported: SupportedTimes::from(u16::from_be_bytes(
2600                        bytes[20..=21].try_into()?,
2601                    )),
2602                    maximum_dmx_fail_hold_time_supported: SupportedTimes::from(u16::from_be_bytes(
2603                        bytes[22..=23].try_into()?,
2604                    )),
2605                    minimum_startup_delay_time_supported: SupportedTimes::from(u16::from_be_bytes(
2606                        bytes[24..=25].try_into()?,
2607                    )),
2608                    maximum_startup_delay_time_supported: SupportedTimes::from(u16::from_be_bytes(
2609                        bytes[26..=27].try_into()?,
2610                    )),
2611                    minimum_startup_hold_time_supported: SupportedTimes::from(u16::from_be_bytes(
2612                        bytes[28..=29].try_into()?,
2613                    )),
2614                    maximum_startup_hold_time_supported: SupportedTimes::from(u16::from_be_bytes(
2615                        bytes[30..=31].try_into()?,
2616                    )),
2617                })
2618            }
2619            (CommandClass::GetCommandResponse, ParameterId::PresetStatus) => {
2620                Ok(Self::GetPresetStatus {
2621                    scene_id: u16::from_be_bytes(bytes[0..=1].try_into()?),
2622                    up_fade_time: u16::from_be_bytes(bytes[2..=3].try_into()?),
2623                    down_fade_time: u16::from_be_bytes(bytes[4..=5].try_into()?),
2624                    wait_time: u16::from_be_bytes(bytes[6..=7].try_into()?),
2625                    programmed: PresetProgrammed::try_from(bytes[8])?,
2626                })
2627            }
2628            (CommandClass::GetCommandResponse, ParameterId::PresetMergeMode) => {
2629                Ok(Self::GetPresetMergeMode(MergeMode::try_from(bytes[0])?))
2630            }
2631            // E1.37-2
2632            (CommandClass::GetCommandResponse, ParameterId::ListInterfaces) => {
2633                Ok(Self::GetListInterfaces(
2634                    #[cfg(feature = "alloc")]
2635                    bytes
2636                        .chunks(6)
2637                        .map(|chunk| {
2638                            Ok(NetworkInterface {
2639                                interface_id: u32::from_be_bytes(chunk[0..=3].try_into()?),
2640                                hardware_type: u16::from_be_bytes(chunk[4..=5].try_into()?).into(),
2641                            })
2642                        })
2643                        .collect::<Result<Vec<NetworkInterface>, RdmError>>()?,
2644                    #[cfg(not(feature = "alloc"))]
2645                    bytes
2646                        .chunks(6)
2647                        .map(|chunk| {
2648                            Ok(NetworkInterface {
2649                                interface_id: u32::from_be_bytes(chunk[0..=3].try_into()?),
2650                                hardware_type: u16::from_be_bytes(chunk[4..=5].try_into()?).into(),
2651                            })
2652                        })
2653                        .collect::<Result<Vec<NetworkInterface, 38>, RdmError>>()?,
2654                ))
2655            }
2656            (CommandClass::GetCommandResponse, ParameterId::InterfaceLabel) => {
2657                Ok(Self::GetInterfaceLabel {
2658                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2659                    interface_label: decode_string_bytes(&bytes[4..])?,
2660                })
2661            }
2662            (CommandClass::GetCommandResponse, ParameterId::InterfaceHardwareAddressType1) => {
2663                Ok(Self::GetInterfaceHardwareAddressType1 {
2664                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2665                    hardware_address: <[u8; 6]>::try_from(&bytes[4..=9])?.into(),
2666                })
2667            }
2668            (CommandClass::GetCommandResponse, ParameterId::IpV4DhcpMode) => {
2669                Ok(Self::GetIpV4DhcpMode {
2670                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2671                    dhcp_mode: bytes[4] == 1,
2672                })
2673            }
2674            (CommandClass::GetCommandResponse, ParameterId::IpV4ZeroConfMode) => {
2675                Ok(Self::GetIpV4ZeroConfMode {
2676                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2677                    zero_conf_mode: bytes[4] == 1,
2678                })
2679            }
2680            (CommandClass::GetCommandResponse, ParameterId::IpV4CurrentAddress) => {
2681                Ok(Self::GetIpV4CurrentAddress {
2682                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2683                    address: <[u8; 4]>::try_from(&bytes[4..=7])?.into(),
2684                    netmask: bytes[8],
2685                    dhcp_status: DhcpMode::try_from(bytes[9])?,
2686                })
2687            }
2688            (CommandClass::GetCommandResponse, ParameterId::IpV4StaticAddress) => {
2689                Ok(Self::GetIpV4StaticAddress {
2690                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2691                    address: <[u8; 4]>::try_from(&bytes[4..=7])?.into(),
2692                    netmask: bytes[8],
2693                })
2694            }
2695            (CommandClass::GetCommandResponse, ParameterId::IpV4DefaultRoute) => {
2696                Ok(Self::GetIpV4DefaultRoute {
2697                    interface_id: u32::from_be_bytes(bytes[0..=3].try_into()?),
2698                    address: <[u8; 4]>::try_from(&bytes[4..=7])?.into(),
2699                })
2700            }
2701            (CommandClass::GetCommandResponse, ParameterId::DnsIpV4NameServer) => {
2702                Ok(Self::GetDnsIpV4NameServer {
2703                    name_server_index: bytes[0],
2704                    address: <[u8; 4]>::try_from(&bytes[1..=4])?.into(),
2705                })
2706            }
2707            // E1.37-7
2708            (CommandClass::GetCommand, ParameterId::EndpointList) => Ok(Self::GetEndpointList {
2709                list_change_number: u32::from_be_bytes(bytes[0..=3].try_into()?),
2710                #[cfg(feature = "alloc")]
2711                endpoint_list: bytes[4..]
2712                    .chunks(3)
2713                    .map(|chunk| {
2714                        Ok((
2715                            u16::from_be_bytes(chunk[0..=1].try_into()?).into(),
2716                            chunk[1].try_into()?,
2717                        ))
2718                    })
2719                    .collect::<Result<Vec<(EndpointId, EndpointType)>, RdmError>>()?,
2720                #[cfg(not(feature = "alloc"))]
2721                endpoint_list: bytes[4..]
2722                    .chunks(6)
2723                    .map(|chunk| {
2724                        Ok((
2725                            u16::from_be_bytes(chunk[0..=1].try_into()?).into(),
2726                            chunk[1].try_into()?,
2727                        ))
2728                    })
2729                    .collect::<Result<Vec<(EndpointId, EndpointType), 75>, RdmError>>()?,
2730            }),
2731            (CommandClass::GetCommand, ParameterId::EndpointListChange) => {
2732                Ok(Self::GetEndpointListChange {
2733                    list_change_number: u32::from_be_bytes(bytes[0..=3].try_into()?),
2734                })
2735            }
2736            (CommandClass::GetCommand, ParameterId::IdentifyEndpoint) => {
2737                Ok(Self::GetIdentifyEndpoint {
2738                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2739                    identify: bytes[2] == 1,
2740                })
2741            }
2742            (CommandClass::SetCommand, ParameterId::IdentifyEndpoint) => {
2743                Ok(Self::SetIdentifyEndpoint {
2744                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2745                })
2746            }
2747            (CommandClass::GetCommand, ParameterId::EndpointToUniverse) => {
2748                Ok(Self::GetEndpointToUniverse {
2749                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2750                    universe: u16::from_be_bytes(bytes[2..=3].try_into()?),
2751                })
2752            }
2753            (CommandClass::SetCommand, ParameterId::EndpointToUniverse) => {
2754                Ok(Self::SetEndpointToUniverse {
2755                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2756                })
2757            }
2758            (CommandClass::GetCommand, ParameterId::EndpointMode) => Ok(Self::GetEndpointMode {
2759                endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2760                mode: bytes[2].try_into()?,
2761            }),
2762            (CommandClass::SetCommand, ParameterId::EndpointMode) => Ok(Self::SetEndpointMode {
2763                endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2764            }),
2765            (CommandClass::GetCommand, ParameterId::EndpointLabel) => Ok(Self::GetEndpointLabel {
2766                endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2767                label: decode_string_bytes(&bytes[2..])?,
2768            }),
2769            (CommandClass::SetCommand, ParameterId::EndpointLabel) => Ok(Self::SetEndpointLabel {
2770                endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2771            }),
2772            (CommandClass::GetCommand, ParameterId::RdmTrafficEnable) => {
2773                Ok(Self::GetRdmTrafficEnable {
2774                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2775                    enable: bytes[2] == 1,
2776                })
2777            }
2778            (CommandClass::SetCommand, ParameterId::RdmTrafficEnable) => {
2779                Ok(Self::SetRdmTrafficEnable {
2780                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2781                })
2782            }
2783            (CommandClass::GetCommand, ParameterId::DiscoveryState) => {
2784                Ok(Self::GetDiscoveryState {
2785                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2786                    device_count: u16::from_be_bytes(bytes[2..=3].try_into()?).into(),
2787                    discovery_state: bytes[4].try_into()?,
2788                })
2789            }
2790            (CommandClass::SetCommand, ParameterId::DiscoveryState) => {
2791                Ok(Self::SetDiscoveryState {
2792                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2793                })
2794            }
2795            (CommandClass::GetCommand, ParameterId::BackgroundDiscovery) => {
2796                Ok(Self::GetBackgroundDiscovery {
2797                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2798                    enabled: bytes[2] == 1,
2799                })
2800            }
2801            (CommandClass::SetCommand, ParameterId::BackgroundDiscovery) => {
2802                Ok(Self::SetBackgroundDiscovery {
2803                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2804                })
2805            }
2806            (CommandClass::GetCommand, ParameterId::EndpointTiming) => {
2807                Ok(Self::GetEndpointTiming {
2808                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2809                    current_setting_id: bytes[2],
2810                    setting_count: bytes[3],
2811                })
2812            }
2813            (CommandClass::SetCommand, ParameterId::EndpointTiming) => {
2814                Ok(Self::SetEndpointTiming {
2815                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2816                })
2817            }
2818            (CommandClass::GetCommand, ParameterId::EndpointTimingDescription) => {
2819                Ok(Self::GetEndpointTimingDescription {
2820                    setting_id: bytes[0],
2821                    description: decode_string_bytes(&bytes[1..])?,
2822                })
2823            }
2824            (CommandClass::GetCommand, ParameterId::EndpointResponders) => {
2825                Ok(Self::GetEndpointResponders {
2826                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2827                    list_change_number: u32::from_be_bytes(bytes[2..=5].try_into()?),
2828                    #[cfg(feature = "alloc")]
2829                    responders: bytes[6..]
2830                        .chunks(6)
2831                        .map(|chunk| {
2832                            Ok(DeviceUID::new(
2833                                u16::from_be_bytes(chunk[0..=1].try_into()?),
2834                                u32::from_be_bytes(chunk[2..=5].try_into()?),
2835                            ))
2836                        })
2837                        .collect::<Result<Vec<DeviceUID>, RdmError>>()?,
2838                    #[cfg(not(feature = "alloc"))]
2839                    responders: bytes[6..]
2840                        .chunks(6)
2841                        .map(|chunk| {
2842                            Ok(DeviceUID::new(
2843                                u16::from_be_bytes(chunk[0..=1].try_into()?),
2844                                u32::from_be_bytes(chunk[2..=5].try_into()?),
2845                            ))
2846                        })
2847                        .collect::<Result<Vec<DeviceUID, 37>, RdmError>>()?,
2848                })
2849            }
2850            (CommandClass::GetCommand, ParameterId::EndpointResponderListChange) => {
2851                Ok(Self::GetEndpointResponderListChange {
2852                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2853                    list_change_number: u32::from_be_bytes(bytes[2..=5].try_into()?),
2854                })
2855            }
2856            (CommandClass::GetCommand, ParameterId::BindingControlFields) => {
2857                Ok(Self::GetBindingControlFields {
2858                    endpoint_id: u16::from_be_bytes(bytes[0..=1].try_into()?).into(),
2859                    uid: DeviceUID::new(
2860                        u16::from_be_bytes(bytes[2..=3].try_into()?),
2861                        u32::from_be_bytes(bytes[4..=7].try_into()?),
2862                    ),
2863                    control_field: u16::from_be_bytes(bytes[8..=9].try_into()?),
2864                    binding_uid: DeviceUID::new(
2865                        u16::from_be_bytes(bytes[10..=11].try_into()?),
2866                        u32::from_be_bytes(bytes[12..=15].try_into()?),
2867                    ),
2868                })
2869            }
2870            (CommandClass::GetCommand, ParameterId::BackgroundQueuedStatusPolicy) => {
2871                Ok(Self::GetBackgroundQueuedStatusPolicy {
2872                    current_policy_id: bytes[0],
2873                    policy_count: bytes[1],
2874                })
2875            }
2876            (CommandClass::GetCommand, ParameterId::BackgroundQueuedStatusPolicyDescription) => {
2877                Ok(Self::GetBackgroundQueuedStatusPolicyDescription {
2878                    policy_id: bytes[0],
2879                    description: decode_string_bytes(&bytes[1..])?,
2880                })
2881            }
2882            // E1.33
2883            (CommandClass::GetCommandResponse, ParameterId::ComponentScope) => {
2884                Ok(Self::GetComponentScope {
2885                    scope_slot: u16::from_be_bytes(bytes[0..=1].try_into()?),
2886                    scope_string: decode_string_bytes(&bytes[2..=64])?,
2887                    static_config_type: bytes[65].try_into()?,
2888                    static_ipv4_address: <[u8; 4]>::try_from(&bytes[66..=69])?.into(),
2889                    static_ipv6_address: <[u8; 16]>::try_from(&bytes[70..=85])?.into(),
2890                    static_port: u16::from_be_bytes(bytes[2..=3].try_into()?),
2891                })
2892            }
2893            (CommandClass::GetCommandResponse, ParameterId::SearchDomain) => {
2894                Ok(Self::GetSearchDomain(decode_string_bytes(bytes)?))
2895            }
2896            (CommandClass::GetCommandResponse, ParameterId::TcpCommsStatus) => {
2897                Ok(Self::GetTcpCommsStatus {
2898                    scope_string: decode_string_bytes(&bytes[0..=62])?,
2899                    broker_ipv4_address: <[u8; 4]>::try_from(&bytes[63..=66])?.into(),
2900                    broker_ipv6_address: <[u8; 16]>::try_from(&bytes[67..=82])?.into(),
2901                    broker_port: u16::from_be_bytes(bytes[83..=84].try_into()?),
2902                    unhealthy_tcp_events: u16::from_be_bytes(bytes[85..=86].try_into()?),
2903                })
2904            }
2905            (CommandClass::GetCommandResponse, ParameterId::BrokerStatus) => {
2906                Ok(Self::GetBrokerStatus {
2907                    is_allowing_set_commands: bytes[0] == 1,
2908                    broker_state: bytes[1].try_into()?,
2909                })
2910            }
2911            (_, ParameterId::ManufacturerSpecific(_)) => Ok(Self::ManufacturerSpecific(
2912                #[cfg(feature = "alloc")]
2913                bytes.to_vec(),
2914                #[cfg(not(feature = "alloc"))]
2915                Vec::<u8, 231>::from_slice(bytes).unwrap(),
2916            )),
2917            (_, _) => Ok(Self::Unsupported(
2918                #[cfg(feature = "alloc")]
2919                bytes.to_vec(),
2920                #[cfg(not(feature = "alloc"))]
2921                Vec::<u8, 231>::from_slice(bytes).unwrap(),
2922            )),
2923        }
2924    }
2925}
2926
2927#[derive(Clone, Debug, PartialEq)]
2928pub struct RdmFrameResponse {
2929    pub destination_uid: DeviceUID,
2930    pub source_uid: DeviceUID,
2931    pub transaction_number: u8,
2932    pub response_type: ResponseType,
2933    pub message_count: u8,
2934    pub sub_device_id: SubDeviceId,
2935    pub command_class: CommandClass,
2936    pub parameter_id: ParameterId,
2937    pub parameter_data: ResponseData,
2938}
2939
2940impl RdmFrameResponse {
2941    pub fn encode(&self) -> EncodedFrame {
2942        let parameter_data = self.parameter_data.encode();
2943
2944        let message_length = 24 + parameter_data.len();
2945
2946        #[cfg(feature = "alloc")]
2947        let mut buf = Vec::with_capacity(message_length + 2);
2948        #[cfg(not(feature = "alloc"))]
2949        let mut buf = Vec::new();
2950
2951        #[cfg(feature = "alloc")]
2952        buf.push(RDM_START_CODE_BYTE);
2953        #[cfg(not(feature = "alloc"))]
2954        buf.push(RDM_START_CODE_BYTE).unwrap();
2955
2956        #[cfg(feature = "alloc")]
2957        buf.push(RDM_SUB_START_CODE_BYTE);
2958        #[cfg(not(feature = "alloc"))]
2959        buf.push(RDM_SUB_START_CODE_BYTE).unwrap();
2960
2961        #[cfg(feature = "alloc")]
2962        buf.push(message_length as u8);
2963        #[cfg(not(feature = "alloc"))]
2964        buf.push(message_length as u8).unwrap();
2965
2966        buf.extend(self.destination_uid.manufacturer_id.to_be_bytes());
2967        buf.extend(self.destination_uid.device_id.to_be_bytes());
2968        buf.extend(self.source_uid.manufacturer_id.to_be_bytes());
2969        buf.extend(self.source_uid.device_id.to_be_bytes());
2970
2971        #[cfg(feature = "alloc")]
2972        buf.push(self.transaction_number);
2973        #[cfg(not(feature = "alloc"))]
2974        buf.push(self.transaction_number).unwrap();
2975
2976        #[cfg(feature = "alloc")]
2977        buf.push(self.response_type as u8);
2978        #[cfg(not(feature = "alloc"))]
2979        buf.push(self.response_type as u8).unwrap();
2980
2981        // Message Count shall be set to 0x00 in all controller generated requests
2982        #[cfg(feature = "alloc")]
2983        buf.push(self.message_count);
2984        #[cfg(not(feature = "alloc"))]
2985        buf.push(self.message_count).unwrap();
2986
2987        buf.extend(u16::from(self.sub_device_id).to_be_bytes());
2988
2989        #[cfg(feature = "alloc")]
2990        buf.push(self.command_class as u8);
2991        #[cfg(not(feature = "alloc"))]
2992        buf.push(self.command_class as u8).unwrap();
2993
2994        buf.extend(u16::from(self.parameter_id).to_be_bytes());
2995
2996        #[cfg(feature = "alloc")]
2997        buf.push(parameter_data.len() as u8);
2998        #[cfg(not(feature = "alloc"))]
2999        buf.push(parameter_data.len() as u8).unwrap();
3000
3001        buf.extend(parameter_data);
3002        buf.extend(bsd_16_crc(&buf[..]).to_be_bytes());
3003
3004        buf
3005    }
3006
3007    pub fn decode(bytes: &[u8]) -> Result<Self, RdmError> {
3008        let message_length = bytes[2];
3009
3010        if message_length < 24 {
3011            return Err(RdmError::InvalidMessageLength(message_length));
3012        }
3013
3014        if bytes.len() < message_length as usize + 2 {
3015            return Err(RdmError::InvalidMessageLength(message_length));
3016        }
3017
3018        let packet_checksum = u16::from_be_bytes(
3019            bytes[message_length as usize..=message_length as usize + 1].try_into()?,
3020        );
3021
3022        let decoded_checksum = bsd_16_crc(&bytes[..message_length as usize]);
3023
3024        if decoded_checksum != packet_checksum {
3025            return Err(RdmError::InvalidChecksum(decoded_checksum, packet_checksum));
3026        }
3027
3028        let destination_uid = DeviceUID::from(<[u8; 6]>::try_from(&bytes[3..=8])?);
3029
3030        let source_uid = DeviceUID::from(<[u8; 6]>::try_from(&bytes[9..=14])?);
3031
3032        let transaction_number = bytes[15];
3033
3034        let response_type = ResponseType::try_from(bytes[16])?;
3035
3036        let message_count = bytes[17];
3037
3038        let sub_device_id = u16::from_be_bytes(bytes[18..=19].try_into()?).into();
3039
3040        let command_class = CommandClass::try_from(bytes[20])?;
3041
3042        let parameter_id = u16::from_be_bytes(bytes[21..=22].try_into()?).into();
3043
3044        let parameter_data_length = bytes[23];
3045
3046        if parameter_data_length > 231 {
3047            return Err(RdmError::InvalidParameterDataLength(parameter_data_length));
3048        }
3049
3050        let parameter_data = ResponseData::decode(
3051            response_type,
3052            command_class,
3053            parameter_data_length,
3054            parameter_id,
3055            &bytes[24..=(24 + parameter_data_length as usize - 1)],
3056        )?;
3057
3058        Ok(Self {
3059            destination_uid,
3060            source_uid,
3061            transaction_number,
3062            response_type,
3063            message_count,
3064            sub_device_id,
3065            command_class,
3066            parameter_id,
3067            parameter_data,
3068        })
3069    }
3070}
3071
3072impl TryFrom<&[u8]> for RdmFrameResponse {
3073    type Error = RdmError;
3074
3075    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
3076        RdmFrameResponse::decode(bytes)
3077    }
3078}
3079
3080#[derive(Copy, Clone, Debug, PartialEq)]
3081pub struct DiscoveryUniqueBranchFrameResponse(pub DeviceUID);
3082
3083impl DiscoveryUniqueBranchFrameResponse {
3084    pub fn encode(&self) -> EncodedFrame {
3085        #[cfg(feature = "alloc")]
3086        let mut buf = Vec::with_capacity(24);
3087        #[cfg(not(feature = "alloc"))]
3088        let mut buf = Vec::new();
3089
3090        buf.extend(iter::repeat(DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE).take(7));
3091
3092        #[cfg(feature = "alloc")]
3093        buf.push(DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE);
3094        #[cfg(not(feature = "alloc"))]
3095        buf.push(DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE)
3096            .unwrap();
3097
3098        let [manufacturer_id1, manufacturer_id0] = self.0.manufacturer_id.to_be_bytes();
3099
3100        buf.extend([
3101            manufacturer_id1 | 0xaa,
3102            manufacturer_id1 | 0x55,
3103            manufacturer_id0 | 0xaa,
3104            manufacturer_id0 | 0x55,
3105        ]);
3106
3107        let [device_id3, device_id2, device_id1, device_id0] = self.0.device_id.to_be_bytes();
3108
3109        buf.extend([
3110            device_id3 | 0xaa,
3111            device_id3 | 0x55,
3112            device_id2 | 0xaa,
3113            device_id2 | 0x55,
3114            device_id1 | 0xaa,
3115            device_id1 | 0x55,
3116            device_id0 | 0xaa,
3117            device_id0 | 0x55,
3118        ]);
3119
3120        let [checksum1, checksum0] = bsd_16_crc(&buf[8..]).to_be_bytes();
3121
3122        buf.extend([
3123            checksum1 | 0xaa,
3124            checksum1 | 0x55,
3125            checksum0 | 0xaa,
3126            checksum0 | 0x55,
3127        ]);
3128
3129        buf
3130    }
3131
3132    pub fn decode(bytes: &[u8]) -> Result<Self, RdmError> {
3133        let Some(frame_start_index) = bytes.iter().position(|&x| x == 0xaa) else {
3134            return Err(RdmError::InvalidDiscoveryUniqueBranchPreamble);
3135        };
3136
3137        let euid = &bytes[(frame_start_index + 1)..=(frame_start_index + 12)];
3138
3139        let ecs = &bytes[(frame_start_index + 13)..=(frame_start_index + 16)];
3140
3141        let decoded_checksum = bsd_16_crc(euid);
3142
3143        let checksum = u16::from_be_bytes([ecs[0] & ecs[1], ecs[2] & ecs[3]]);
3144
3145        if checksum != decoded_checksum {
3146            return Err(RdmError::InvalidChecksum(decoded_checksum, checksum));
3147        }
3148
3149        let manufacturer_id = u16::from_be_bytes([euid[0] & euid[1], euid[2] & euid[3]]);
3150
3151        let device_id = u32::from_be_bytes([
3152            euid[4] & euid[5],
3153            euid[6] & euid[7],
3154            euid[8] & euid[9],
3155            euid[10] & euid[11],
3156        ]);
3157
3158        Ok(Self(DeviceUID::new(manufacturer_id, device_id)))
3159    }
3160}
3161
3162impl TryFrom<&[u8]> for DiscoveryUniqueBranchFrameResponse {
3163    type Error = RdmError;
3164
3165    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
3166        DiscoveryUniqueBranchFrameResponse::decode(bytes)
3167    }
3168}
3169
3170#[allow(clippy::large_enum_variant)]
3171#[derive(Clone, Debug, PartialEq)]
3172pub enum RdmResponse {
3173    RdmFrame(RdmFrameResponse),
3174    DiscoveryUniqueBranchFrame(DiscoveryUniqueBranchFrameResponse),
3175}
3176
3177impl RdmResponse {
3178    pub fn encode(&self) -> EncodedFrame {
3179        match self {
3180            RdmResponse::RdmFrame(frame) => frame.encode(),
3181            RdmResponse::DiscoveryUniqueBranchFrame(frame) => frame.encode(),
3182        }
3183    }
3184
3185    pub fn decode(bytes: &[u8]) -> Result<Self, RdmError> {
3186        if bytes[0] == RDM_START_CODE_BYTE && bytes[1] == RDM_SUB_START_CODE_BYTE {
3187            if bytes.len() < 25 {
3188                return Err(RdmError::InvalidFrameLength(bytes.len() as u8));
3189            }
3190
3191            return RdmFrameResponse::decode(bytes).map(RdmResponse::RdmFrame);
3192        }
3193
3194        if bytes[0] == DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE
3195            || bytes[0] == DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE
3196        {
3197            if bytes.len() < 17 {
3198                return Err(RdmError::InvalidFrameLength(bytes.len() as u8));
3199            }
3200
3201            return DiscoveryUniqueBranchFrameResponse::decode(bytes)
3202                .map(RdmResponse::DiscoveryUniqueBranchFrame);
3203        }
3204
3205        Err(RdmError::InvalidStartCode)
3206    }
3207}
3208
3209impl TryFrom<&[u8]> for RdmResponse {
3210    type Error = RdmError;
3211
3212    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
3213        RdmResponse::decode(bytes)
3214    }
3215}
3216
3217#[cfg(test)]
3218mod tests {
3219    use super::*;
3220
3221    #[test]
3222    fn should_decode_valid_rdm_ack_response() {
3223        let decoded = RdmResponse::decode(&[
3224            0xcc, // Start Code
3225            0x01, // Sub Start Code
3226            25,   // Message Length
3227            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3228            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3229            0x00, // Transaction Number
3230            0x00, // Response Type = Ack
3231            0x00, // Message Count
3232            0x00, 0x00, // Sub-Device ID = Root Device
3233            0x21, // Command Class = GetCommandResponse
3234            0x10, 0x00, // Parameter ID = Identify Device
3235            0x01, // PDL
3236            0x01, // Identifying = true
3237            0x01, 0x43, // Checksum
3238        ]);
3239
3240        let expected = Ok(RdmResponse::RdmFrame(RdmFrameResponse {
3241            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3242            source_uid: DeviceUID::new(0x0605, 0x04030201),
3243            transaction_number: 0x00,
3244            response_type: ResponseType::Ack,
3245            message_count: 0x00,
3246            sub_device_id: SubDeviceId::RootDevice,
3247            command_class: CommandClass::GetCommandResponse,
3248            parameter_id: ParameterId::IdentifyDevice,
3249            parameter_data: ResponseData::ParameterData(Some(
3250                ResponseParameterData::GetIdentifyDevice(true),
3251            )),
3252        }));
3253
3254        assert_eq!(decoded, expected);
3255    }
3256
3257    #[test]
3258    fn should_encode_valid_rdm_ack_response() {
3259        let encoded = RdmResponse::RdmFrame(RdmFrameResponse {
3260            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3261            source_uid: DeviceUID::new(0x0605, 0x04030201),
3262            transaction_number: 0x00,
3263            response_type: ResponseType::Ack,
3264            message_count: 0x00,
3265            sub_device_id: SubDeviceId::RootDevice,
3266            command_class: CommandClass::GetCommandResponse,
3267            parameter_id: ParameterId::IdentifyDevice,
3268            parameter_data: ResponseData::ParameterData(Some(
3269                ResponseParameterData::GetIdentifyDevice(true),
3270            )),
3271        })
3272        .encode();
3273
3274        let expected = &[
3275            0xcc, // Start Code
3276            0x01, // Sub Start Code
3277            25,   // Message Length
3278            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3279            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3280            0x00, // Transaction Number
3281            0x00, // Response Type = Ack
3282            0x00, // Message Count
3283            0x00, 0x00, // Sub-Device ID = Root Device
3284            0x21, // Command Class = GetCommandResponse
3285            0x10, 0x00, // Parameter ID = Identify Device
3286            0x01, // PDL
3287            0x01, // Identifying = true
3288            0x01, 0x43, // Checksum
3289        ];
3290
3291        assert_eq!(encoded, expected);
3292    }
3293
3294    #[test]
3295    fn should_decode_valid_rdm_ack_manufacturer_specific_response() {
3296        let decoded = RdmResponse::decode(&[
3297            0xcc, // Start Code
3298            0x01, // Sub Start Code
3299            28,   // Message Length
3300            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3301            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3302            0x00, // Transaction Number
3303            0x00, // Response Type = Ack
3304            0x00, // Message Count
3305            0x00, 0x00, // Sub-Device ID = Root Device
3306            0x31, // Command Class = SetCommandResponse
3307            0x80, 0x80, // Parameter ID = Identify Device
3308            0x04, // PDL
3309            0x04, 0x03, 0x02, 0x01, // Arbitrary manufacturer specific data
3310            0x02, 0x52, // Checksum
3311        ]);
3312
3313        let expected = Ok(RdmResponse::RdmFrame(RdmFrameResponse {
3314            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3315            source_uid: DeviceUID::new(0x0605, 0x04030201),
3316            transaction_number: 0x00,
3317            response_type: ResponseType::Ack,
3318            message_count: 0x00,
3319            sub_device_id: SubDeviceId::RootDevice,
3320            command_class: CommandClass::SetCommandResponse,
3321            parameter_id: ParameterId::ManufacturerSpecific(0x8080),
3322            #[cfg(feature = "alloc")]
3323            parameter_data: ResponseData::ParameterData(Some(
3324                ResponseParameterData::ManufacturerSpecific(vec![0x04, 0x03, 0x02, 0x01]),
3325            )),
3326            #[cfg(not(feature = "alloc"))]
3327            parameter_data: ResponseData::ParameterData(Some(
3328                ResponseParameterData::ManufacturerSpecific(
3329                    Vec::<u8, 231>::from_slice(&[0x04, 0x03, 0x02, 0x01]).unwrap(),
3330                ),
3331            )),
3332        }));
3333
3334        assert_eq!(decoded, expected);
3335    }
3336
3337    #[test]
3338    fn should_encode_valid_rdm_ack_manufacturer_specific_response() {
3339        let encoded = RdmResponse::RdmFrame(RdmFrameResponse {
3340            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3341            source_uid: DeviceUID::new(0x0605, 0x04030201),
3342            transaction_number: 0x00,
3343            response_type: ResponseType::Ack,
3344            message_count: 0x00,
3345            sub_device_id: SubDeviceId::RootDevice,
3346            command_class: CommandClass::SetCommandResponse,
3347            parameter_id: ParameterId::ManufacturerSpecific(0x8080),
3348            #[cfg(feature = "alloc")]
3349            parameter_data: ResponseData::ParameterData(Some(
3350                ResponseParameterData::ManufacturerSpecific(vec![0x04, 0x03, 0x02, 0x01]),
3351            )),
3352            #[cfg(not(feature = "alloc"))]
3353            parameter_data: ResponseData::ParameterData(Some(
3354                ResponseParameterData::ManufacturerSpecific(
3355                    Vec::<u8, 231>::from_slice(&[0x04, 0x03, 0x02, 0x01]).unwrap(),
3356                ),
3357            )),
3358        })
3359        .encode();
3360
3361        let expected = &[
3362            0xcc, // Start Code
3363            0x01, // Sub Start Code
3364            28,   // Message Length
3365            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3366            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3367            0x00, // Transaction Number
3368            0x00, // Response Type = Ack
3369            0x00, // Message Count
3370            0x00, 0x00, // Sub-Device ID = Root Device
3371            0x31, // Command Class = SetCommandResponse
3372            0x80, 0x80, // Parameter ID = Identify Device
3373            0x04, // PDL
3374            0x04, 0x03, 0x02, 0x01, // Arbitrary manufacturer specific data
3375            0x02, 0x52, // Checksum
3376        ];
3377
3378        assert_eq!(encoded, expected);
3379    }
3380
3381    #[test]
3382    fn should_decode_valid_rdm_ack_timer_response() {
3383        let decoded = RdmResponse::decode(&[
3384            0xcc, // Start Code
3385            0x01, // Sub Start Code
3386            26,   // Message Length
3387            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3388            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3389            0x00, // Transaction Number
3390            0x01, // Response Type = AckTimer
3391            0x00, // Message Count
3392            0x00, 0x00, // Sub-Device ID = Root Device
3393            0x21, // Command Class = GetCommandResponse
3394            0x10, 0x00, // Parameter ID = Identify Device
3395            0x02, // PDL
3396            0x00, 0x0a, // Estimated Response Time = 10x 100ms = 1 second
3397            0x01, 0x4f, // Checksum
3398        ]);
3399
3400        let expected = Ok(RdmResponse::RdmFrame(RdmFrameResponse {
3401            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3402            source_uid: DeviceUID::new(0x0605, 0x04030201),
3403            transaction_number: 0x00,
3404            response_type: ResponseType::AckTimer,
3405            message_count: 0x00,
3406            sub_device_id: SubDeviceId::RootDevice,
3407            command_class: CommandClass::GetCommandResponse,
3408            parameter_id: ParameterId::IdentifyDevice,
3409            parameter_data: ResponseData::EstimateResponseTime(0x0a),
3410        }));
3411
3412        assert_eq!(decoded, expected);
3413    }
3414
3415    #[test]
3416    fn should_encode_valid_rdm_ack_timer_response() {
3417        let encoded = RdmResponse::RdmFrame(RdmFrameResponse {
3418            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3419            source_uid: DeviceUID::new(0x0605, 0x04030201),
3420            transaction_number: 0x00,
3421            response_type: ResponseType::AckTimer,
3422            message_count: 0x00,
3423            sub_device_id: SubDeviceId::RootDevice,
3424            command_class: CommandClass::GetCommandResponse,
3425            parameter_id: ParameterId::IdentifyDevice,
3426            parameter_data: ResponseData::EstimateResponseTime(0x0a),
3427        })
3428        .encode();
3429
3430        let expected = &[
3431            0xcc, // Start Code
3432            0x01, // Sub Start Code
3433            26,   // Message Length
3434            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3435            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3436            0x00, // Transaction Number
3437            0x01, // Response Type = AckTimer
3438            0x00, // Message Count
3439            0x00, 0x00, // Sub-Device ID = Root Device
3440            0x21, // Command Class = GetCommandResponse
3441            0x10, 0x00, // Parameter ID = Identify Device
3442            0x02, // PDL
3443            0x00, 0x0a, // Estimated Response Time = 10x 100ms = 1 second
3444            0x01, 0x4f, // Checksum
3445        ];
3446
3447        assert_eq!(encoded, expected);
3448    }
3449
3450    #[test]
3451    fn should_decode_valid_rdm_nack_reason_response() {
3452        let decoded = RdmResponse::decode(&[
3453            0xcc, // Start Code
3454            0x01, // Sub Start Code
3455            26,   // Message Length
3456            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3457            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3458            0x00, // Transaction Number
3459            0x02, // Response Type = Nack_Reason
3460            0x00, // Message Count
3461            0x00, 0x00, // Sub-Device ID = Root Device
3462            0x21, // Command Class = GetCommandResponse
3463            0x10, 0x00, // Parameter ID = Identify Device
3464            0x02, // PDL
3465            0x00, 0x01, // Nack Reason = FormatError
3466            0x01, 0x47, // Checksum
3467        ]);
3468
3469        let expected = Ok(RdmResponse::RdmFrame(RdmFrameResponse {
3470            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3471            source_uid: DeviceUID::new(0x0605, 0x04030201),
3472            transaction_number: 0x00,
3473            response_type: ResponseType::NackReason,
3474            message_count: 0x00,
3475            sub_device_id: SubDeviceId::RootDevice,
3476            command_class: CommandClass::GetCommandResponse,
3477            parameter_id: ParameterId::IdentifyDevice,
3478            parameter_data: ResponseData::NackReason(ResponseNackReasonCode::FormatError),
3479        }));
3480
3481        assert_eq!(decoded, expected);
3482    }
3483
3484    #[test]
3485    fn should_encode_valid_rdm_nack_reason_response() {
3486        let encoded = RdmResponse::RdmFrame(RdmFrameResponse {
3487            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3488            source_uid: DeviceUID::new(0x0605, 0x04030201),
3489            transaction_number: 0x00,
3490            response_type: ResponseType::NackReason,
3491            message_count: 0x00,
3492            sub_device_id: SubDeviceId::RootDevice,
3493            command_class: CommandClass::GetCommandResponse,
3494            parameter_id: ParameterId::IdentifyDevice,
3495            parameter_data: ResponseData::NackReason(ResponseNackReasonCode::FormatError),
3496        })
3497        .encode();
3498
3499        let expected = &[
3500            0xcc, // Start Code
3501            0x01, // Sub Start Code
3502            26,   // Message Length
3503            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3504            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3505            0x00, // Transaction Number
3506            0x02, // Response Type = Nack_Reason
3507            0x00, // Message Count
3508            0x00, 0x00, // Sub-Device ID = Root Device
3509            0x21, // Command Class = GetCommandResponse
3510            0x10, 0x00, // Parameter ID = Identify Device
3511            0x02, // PDL
3512            0x00, 0x01, // Nack Reason = FormatError
3513            0x01, 0x47, // Checksum
3514        ];
3515
3516        assert_eq!(encoded, expected);
3517    }
3518
3519    #[test]
3520    fn should_decode_valid_rdm_ack_overflow_response() {
3521        let decoded = RdmResponse::decode(&[
3522            0xcc, // Start Code
3523            0x01, // Sub Start Code
3524            25,   // Message Length
3525            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3526            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3527            0x00, // Transaction Number
3528            0x03, // Response Type = Ack_Overflow
3529            0x00, // Message Count
3530            0x00, 0x00, // Sub-Device ID = Root Device
3531            0x21, // Command Class = GetCommandResponse
3532            0x10, 0x00, // Parameter ID = Identify Device
3533            0x01, // PDL
3534            0x01, // Identifying = true
3535            0x01, 0x46, // Checksum
3536        ]);
3537
3538        let expected = Ok(RdmResponse::RdmFrame(RdmFrameResponse {
3539            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3540            source_uid: DeviceUID::new(0x0605, 0x04030201),
3541            transaction_number: 0x00,
3542            response_type: ResponseType::AckOverflow,
3543            message_count: 0x00,
3544            sub_device_id: SubDeviceId::RootDevice,
3545            command_class: CommandClass::GetCommandResponse,
3546            parameter_id: ParameterId::IdentifyDevice,
3547            parameter_data: ResponseData::ParameterData(Some(
3548                ResponseParameterData::GetIdentifyDevice(true),
3549            )),
3550        }));
3551
3552        assert_eq!(decoded, expected);
3553    }
3554
3555    #[test]
3556    fn should_encode_valid_rdm_ack_overflow_response() {
3557        let encoded = RdmResponse::RdmFrame(RdmFrameResponse {
3558            destination_uid: DeviceUID::new(0x0102, 0x03040506),
3559            source_uid: DeviceUID::new(0x0605, 0x04030201),
3560            transaction_number: 0x00,
3561            response_type: ResponseType::AckOverflow,
3562            message_count: 0x00,
3563            sub_device_id: SubDeviceId::RootDevice,
3564            command_class: CommandClass::GetCommandResponse,
3565            parameter_id: ParameterId::IdentifyDevice,
3566            parameter_data: ResponseData::ParameterData(Some(
3567                ResponseParameterData::GetIdentifyDevice(true),
3568            )),
3569        })
3570        .encode();
3571
3572        let expected = &[
3573            0xcc, // Start Code
3574            0x01, // Sub Start Code
3575            25,   // Message Length
3576            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // Destination UID
3577            0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // Source UID
3578            0x00, // Transaction Number
3579            0x03, // Response Type = Ack_Overflow
3580            0x00, // Message Count
3581            0x00, 0x00, // Sub-Device ID = Root Device
3582            0x21, // Command Class = GetCommandResponse
3583            0x10, 0x00, // Parameter ID = Identify Device
3584            0x01, // PDL
3585            0x01, // Identifying = true
3586            0x01, 0x46, // Checksum
3587        ];
3588
3589        assert_eq!(encoded, expected);
3590    }
3591
3592    #[test]
3593    fn should_decode_valid_discovery_unique_branch_response() {
3594        // includes preamble bytes
3595        let decoded = RdmResponse::decode(&[
3596            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3597            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3598            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3599            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3600            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3601            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3602            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3603            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE,
3604            0xab, // euid 11 = manufacturer id 1 (MSB)
3605            0x55, // euid 10 = manufacturer id 1 (MSB)
3606            0xaa, // euid 9 = manufacturer id 0 (LSB)
3607            0x57, // euid 8 = manufacturer id 0 (LSB)
3608            0xab, // euid 7 = device id 3 (MSB)
3609            0x57, // euid 6 = device id 3 (MSB)
3610            0xae, // euid 5 = device id 2
3611            0x55, // euid 4 = device id 2
3612            0xaf, // euid 3 = device id 1
3613            0x55, // euid 2 = device id 1
3614            0xae, // euid 1 = device id 0 (LSB)
3615            0x57, // euid 0 = device id 0 (LSB)
3616            0xae, // ecs 3 = Checksum1 (MSB)
3617            0x57, // ecs 2 = Checksum1 (MSB)
3618            0xaf, // ecs 1 = Checksum0 (LSB)
3619            0x5f, // ecs 0 = Checksum0 (LSB)
3620        ]);
3621
3622        let expected = Ok(RdmResponse::DiscoveryUniqueBranchFrame(
3623            DiscoveryUniqueBranchFrameResponse(DeviceUID::new(0x0102, 0x03040506)),
3624        ));
3625
3626        assert_eq!(decoded, expected);
3627
3628        // does not include preamble bytes
3629        let decoded = RdmResponse::decode(&[
3630            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE,
3631            0xab, // euid 11 = manufacturer id 1 (MSB)
3632            0x55, // euid 10 = manufacturer id 1 (MSB)
3633            0xaa, // euid 9 = manufacturer id 0 (LSB)
3634            0x57, // euid 8 = manufacturer id 0 (LSB)
3635            0xab, // euid 7 = device id 3 (MSB)
3636            0x57, // euid 6 = device id 3 (MSB)
3637            0xae, // euid 5 = device id 2
3638            0x55, // euid 4 = device id 2
3639            0xaf, // euid 3 = device id 1
3640            0x55, // euid 2 = device id 1
3641            0xae, // euid 1 = device id 0 (LSB)
3642            0x57, // euid 0 = device id 0 (LSB)
3643            0xae, // ecs 3 = Checksum1 (MSB)
3644            0x57, // ecs 2 = Checksum1 (MSB)
3645            0xaf, // ecs 1 = Checksum0 (LSB)
3646            0x5f, // ecs 0 = Checksum0 (LSB)
3647        ]);
3648
3649        let expected = Ok(RdmResponse::DiscoveryUniqueBranchFrame(
3650            DiscoveryUniqueBranchFrameResponse(DeviceUID::new(0x0102, 0x03040506)),
3651        ));
3652
3653        assert_eq!(decoded, expected);
3654    }
3655
3656    #[test]
3657    fn should_encode_valid_discovery_unique_branch_response() {
3658        let encoded = RdmResponse::DiscoveryUniqueBranchFrame(DiscoveryUniqueBranchFrameResponse(
3659            DeviceUID::new(0x0102, 0x03040506),
3660        ))
3661        .encode();
3662
3663        let expected = &[
3664            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3665            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3666            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3667            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3668            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3669            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3670            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_BYTE,
3671            DISCOVERY_UNIQUE_BRANCH_PREAMBLE_SEPARATOR_BYTE,
3672            0xab, // euid 11 = manufacturer id 1 (MSB)
3673            0x55, // euid 10 = manufacturer id 1 (MSB)
3674            0xaa, // euid 9 = manufacturer id 0 (LSB)
3675            0x57, // euid 8 = manufacturer id 0 (LSB)
3676            0xab, // euid 7 = device id 3 (MSB)
3677            0x57, // euid 6 = device id 3 (MSB)
3678            0xae, // euid 5 = device id 2
3679            0x55, // euid 4 = device id 2
3680            0xaf, // euid 3 = device id 1
3681            0x55, // euid 2 = device id 1
3682            0xae, // euid 1 = device id 0 (LSB)
3683            0x57, // euid 0 = device id 0 (LSB)
3684            0xae, // ecs 3 = Checksum1 (MSB)
3685            0x57, // ecs 2 = Checksum1 (MSB)
3686            0xaf, // ecs 1 = Checksum0 (LSB)
3687            0x5f, // ecs 0 = Checksum0 (LSB)
3688        ];
3689
3690        assert_eq!(encoded, expected);
3691    }
3692}