matc/clusters/codec/
camera_av_stream_management.rs

1//! Matter TLV encoders and decoders for Camera AV Stream Management Cluster
2//! Cluster ID: 0x0551
3//!
4//! This file is automatically generated from CameraAVStreamManagement.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Import serialization helpers for octet strings
12use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14// Enum definitions
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum AudioCodec {
19    /// Open source IETF standard codec.
20    Opus = 0,
21    /// Advanced Audio Coding codec-Low Complexity
22    AacLc = 1,
23}
24
25impl AudioCodec {
26    /// Convert from u8 value
27    pub fn from_u8(value: u8) -> Option<Self> {
28        match value {
29            0 => Some(AudioCodec::Opus),
30            1 => Some(AudioCodec::AacLc),
31            _ => None,
32        }
33    }
34
35    /// Convert to u8 value
36    pub fn to_u8(self) -> u8 {
37        self as u8
38    }
39}
40
41impl From<AudioCodec> for u8 {
42    fn from(val: AudioCodec) -> Self {
43        val as u8
44    }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48#[repr(u8)]
49pub enum ImageCodec {
50    /// JPEG image codec.
51    Jpeg = 0,
52    /// HEIC image codec.
53    Heic = 1,
54}
55
56impl ImageCodec {
57    /// Convert from u8 value
58    pub fn from_u8(value: u8) -> Option<Self> {
59        match value {
60            0 => Some(ImageCodec::Jpeg),
61            1 => Some(ImageCodec::Heic),
62            _ => None,
63        }
64    }
65
66    /// Convert to u8 value
67    pub fn to_u8(self) -> u8 {
68        self as u8
69    }
70}
71
72impl From<ImageCodec> for u8 {
73    fn from(val: ImageCodec) -> Self {
74        val as u8
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
79#[repr(u8)]
80pub enum TriStateAuto {
81    /// Off
82    Off = 0,
83    /// On
84    On = 1,
85    /// Automatic Operation
86    Auto = 2,
87}
88
89impl TriStateAuto {
90    /// Convert from u8 value
91    pub fn from_u8(value: u8) -> Option<Self> {
92        match value {
93            0 => Some(TriStateAuto::Off),
94            1 => Some(TriStateAuto::On),
95            2 => Some(TriStateAuto::Auto),
96            _ => None,
97        }
98    }
99
100    /// Convert to u8 value
101    pub fn to_u8(self) -> u8 {
102        self as u8
103    }
104}
105
106impl From<TriStateAuto> for u8 {
107    fn from(val: TriStateAuto) -> Self {
108        val as u8
109    }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
113#[repr(u8)]
114pub enum TwoWayTalkSupportType {
115    /// Two-way Talk support is absent.
116    Notsupported = 0,
117    /// Audio in one direction at a time.
118    Halfduplex = 1,
119    /// Audio in both directions simultaneously.
120    Fullduplex = 2,
121}
122
123impl TwoWayTalkSupportType {
124    /// Convert from u8 value
125    pub fn from_u8(value: u8) -> Option<Self> {
126        match value {
127            0 => Some(TwoWayTalkSupportType::Notsupported),
128            1 => Some(TwoWayTalkSupportType::Halfduplex),
129            2 => Some(TwoWayTalkSupportType::Fullduplex),
130            _ => None,
131        }
132    }
133
134    /// Convert to u8 value
135    pub fn to_u8(self) -> u8 {
136        self as u8
137    }
138}
139
140impl From<TwoWayTalkSupportType> for u8 {
141    fn from(val: TwoWayTalkSupportType) -> Self {
142        val as u8
143    }
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
147#[repr(u8)]
148pub enum VideoCodec {
149    /// Advanced Video Coding (H.264) codec.
150    H264 = 0,
151    /// High efficiency Video Coding (H.265) codec.
152    Hevc = 1,
153    /// Versatile Video Coding (H.266) codec.
154    Vvc = 2,
155    /// AOMedia Video 1 codec.
156    Av1 = 3,
157}
158
159impl VideoCodec {
160    /// Convert from u8 value
161    pub fn from_u8(value: u8) -> Option<Self> {
162        match value {
163            0 => Some(VideoCodec::H264),
164            1 => Some(VideoCodec::Hevc),
165            2 => Some(VideoCodec::Vvc),
166            3 => Some(VideoCodec::Av1),
167            _ => None,
168        }
169    }
170
171    /// Convert to u8 value
172    pub fn to_u8(self) -> u8 {
173        self as u8
174    }
175}
176
177impl From<VideoCodec> for u8 {
178    fn from(val: VideoCodec) -> Self {
179        val as u8
180    }
181}
182
183// Struct definitions
184
185#[derive(Debug, serde::Serialize)]
186pub struct AVMetadata {
187    pub utc_time: Option<u64>,
188    pub motion_zones_active: Option<Vec<u8>>,
189    pub black_and_white_active: Option<bool>,
190    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
191    pub user_defined: Option<Vec<u8>>,
192}
193
194#[derive(Debug, serde::Serialize)]
195pub struct AudioCapabilities {
196    pub max_number_of_channels: Option<u8>,
197    pub supported_codecs: Option<Vec<AudioCodec>>,
198    pub supported_sample_rates: Option<Vec<u32>>,
199    pub supported_bit_depths: Option<Vec<u8>>,
200}
201
202#[derive(Debug, serde::Serialize)]
203pub struct AudioStream {
204    pub audio_stream_id: Option<u8>,
205    pub stream_usage: Option<u8>,
206    pub audio_codec: Option<AudioCodec>,
207    pub channel_count: Option<u8>,
208    pub sample_rate: Option<u32>,
209    pub bit_rate: Option<u32>,
210    pub bit_depth: Option<u8>,
211    pub reference_count: Option<u8>,
212}
213
214#[derive(Debug, serde::Serialize)]
215pub struct RateDistortionTradeOffPoints {
216    pub codec: Option<VideoCodec>,
217    pub resolution: Option<VideoResolution>,
218    pub min_bit_rate: Option<u32>,
219}
220
221#[derive(Debug, serde::Serialize)]
222pub struct SnapshotCapabilities {
223    pub resolution: Option<VideoResolution>,
224    pub max_frame_rate: Option<u16>,
225    pub image_codec: Option<ImageCodec>,
226    pub requires_encoded_pixels: Option<bool>,
227    pub requires_hardware_encoder: Option<bool>,
228}
229
230#[derive(Debug, serde::Serialize)]
231pub struct SnapshotStream {
232    pub snapshot_stream_id: Option<u8>,
233    pub image_codec: Option<ImageCodec>,
234    pub frame_rate: Option<u16>,
235    pub min_resolution: Option<VideoResolution>,
236    pub max_resolution: Option<VideoResolution>,
237    pub quality: Option<u8>,
238    pub reference_count: Option<u8>,
239    pub encoded_pixels: Option<bool>,
240    pub hardware_encoder: Option<bool>,
241    pub watermark_enabled: Option<bool>,
242    pub osd_enabled: Option<bool>,
243}
244
245#[derive(Debug, serde::Serialize)]
246pub struct VideoResolution {
247    pub width: Option<u16>,
248    pub height: Option<u16>,
249}
250
251#[derive(Debug, serde::Serialize)]
252pub struct VideoSensorParams {
253    pub sensor_width: Option<u16>,
254    pub sensor_height: Option<u16>,
255    pub max_fps: Option<u16>,
256    pub max_hdrfps: Option<u16>,
257}
258
259#[derive(Debug, serde::Serialize)]
260pub struct VideoStream {
261    pub video_stream_id: Option<u8>,
262    pub stream_usage: Option<u8>,
263    pub video_codec: Option<VideoCodec>,
264    pub min_frame_rate: Option<u16>,
265    pub max_frame_rate: Option<u16>,
266    pub min_resolution: Option<VideoResolution>,
267    pub max_resolution: Option<VideoResolution>,
268    pub min_bit_rate: Option<u32>,
269    pub max_bit_rate: Option<u32>,
270    pub key_frame_interval: Option<u16>,
271    pub watermark_enabled: Option<bool>,
272    pub osd_enabled: Option<bool>,
273    pub reference_count: Option<u8>,
274}
275
276// Command encoders
277
278/// Encode AudioStreamAllocate command (0x00)
279pub fn encode_audio_stream_allocate(stream_usage: u8, audio_codec: AudioCodec, channel_count: u8, sample_rate: u32, bit_rate: u32, bit_depth: u8) -> anyhow::Result<Vec<u8>> {
280    let tlv = tlv::TlvItemEnc {
281        tag: 0,
282        value: tlv::TlvItemValueEnc::StructInvisible(vec![
283        (0, tlv::TlvItemValueEnc::UInt8(stream_usage)).into(),
284        (1, tlv::TlvItemValueEnc::UInt8(audio_codec.to_u8())).into(),
285        (2, tlv::TlvItemValueEnc::UInt8(channel_count)).into(),
286        (3, tlv::TlvItemValueEnc::UInt32(sample_rate)).into(),
287        (4, tlv::TlvItemValueEnc::UInt32(bit_rate)).into(),
288        (5, tlv::TlvItemValueEnc::UInt8(bit_depth)).into(),
289        ]),
290    };
291    Ok(tlv.encode()?)
292}
293
294/// Encode AudioStreamDeallocate command (0x02)
295pub fn encode_audio_stream_deallocate(audio_stream_id: u8) -> anyhow::Result<Vec<u8>> {
296    let tlv = tlv::TlvItemEnc {
297        tag: 0,
298        value: tlv::TlvItemValueEnc::StructInvisible(vec![
299        (0, tlv::TlvItemValueEnc::UInt8(audio_stream_id)).into(),
300        ]),
301    };
302    Ok(tlv.encode()?)
303}
304
305/// Parameters for VideoStreamAllocate command
306pub struct VideoStreamAllocateParams {
307    pub stream_usage: u8,
308    pub video_codec: VideoCodec,
309    pub min_frame_rate: u16,
310    pub max_frame_rate: u16,
311    pub min_resolution: VideoResolution,
312    pub max_resolution: VideoResolution,
313    pub min_bit_rate: u32,
314    pub max_bit_rate: u32,
315    pub key_frame_interval: u16,
316    pub watermark_enabled: bool,
317    pub osd_enabled: bool,
318}
319
320/// Encode VideoStreamAllocate command (0x03)
321pub fn encode_video_stream_allocate(params: VideoStreamAllocateParams) -> anyhow::Result<Vec<u8>> {
322            // Encode struct VideoResolutionStruct
323            let mut min_resolution_fields = Vec::new();
324            if let Some(x) = params.min_resolution.width { min_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
325            if let Some(x) = params.min_resolution.height { min_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
326            // Encode struct VideoResolutionStruct
327            let mut max_resolution_fields = Vec::new();
328            if let Some(x) = params.max_resolution.width { max_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
329            if let Some(x) = params.max_resolution.height { max_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
330    let tlv = tlv::TlvItemEnc {
331        tag: 0,
332        value: tlv::TlvItemValueEnc::StructInvisible(vec![
333        (0, tlv::TlvItemValueEnc::UInt8(params.stream_usage)).into(),
334        (1, tlv::TlvItemValueEnc::UInt8(params.video_codec.to_u8())).into(),
335        (2, tlv::TlvItemValueEnc::UInt16(params.min_frame_rate)).into(),
336        (3, tlv::TlvItemValueEnc::UInt16(params.max_frame_rate)).into(),
337        (4, tlv::TlvItemValueEnc::StructInvisible(min_resolution_fields)).into(),
338        (5, tlv::TlvItemValueEnc::StructInvisible(max_resolution_fields)).into(),
339        (6, tlv::TlvItemValueEnc::UInt32(params.min_bit_rate)).into(),
340        (7, tlv::TlvItemValueEnc::UInt32(params.max_bit_rate)).into(),
341        (8, tlv::TlvItemValueEnc::UInt16(params.key_frame_interval)).into(),
342        (9, tlv::TlvItemValueEnc::Bool(params.watermark_enabled)).into(),
343        (10, tlv::TlvItemValueEnc::Bool(params.osd_enabled)).into(),
344        ]),
345    };
346    Ok(tlv.encode()?)
347}
348
349/// Encode VideoStreamModify command (0x05)
350pub fn encode_video_stream_modify(video_stream_id: u8, watermark_enabled: bool, osd_enabled: bool) -> anyhow::Result<Vec<u8>> {
351    let tlv = tlv::TlvItemEnc {
352        tag: 0,
353        value: tlv::TlvItemValueEnc::StructInvisible(vec![
354        (0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
355        (1, tlv::TlvItemValueEnc::Bool(watermark_enabled)).into(),
356        (2, tlv::TlvItemValueEnc::Bool(osd_enabled)).into(),
357        ]),
358    };
359    Ok(tlv.encode()?)
360}
361
362/// Encode VideoStreamDeallocate command (0x06)
363pub fn encode_video_stream_deallocate(video_stream_id: u8) -> anyhow::Result<Vec<u8>> {
364    let tlv = tlv::TlvItemEnc {
365        tag: 0,
366        value: tlv::TlvItemValueEnc::StructInvisible(vec![
367        (0, tlv::TlvItemValueEnc::UInt8(video_stream_id)).into(),
368        ]),
369    };
370    Ok(tlv.encode()?)
371}
372
373/// Encode SnapshotStreamAllocate command (0x07)
374pub fn encode_snapshot_stream_allocate(image_codec: ImageCodec, max_frame_rate: u16, min_resolution: VideoResolution, max_resolution: VideoResolution, quality: u8, watermark_enabled: bool, osd_enabled: bool) -> anyhow::Result<Vec<u8>> {
375            // Encode struct VideoResolutionStruct
376            let mut min_resolution_fields = Vec::new();
377            if let Some(x) = min_resolution.width { min_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
378            if let Some(x) = min_resolution.height { min_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
379            // Encode struct VideoResolutionStruct
380            let mut max_resolution_fields = Vec::new();
381            if let Some(x) = max_resolution.width { max_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
382            if let Some(x) = max_resolution.height { max_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
383    let tlv = tlv::TlvItemEnc {
384        tag: 0,
385        value: tlv::TlvItemValueEnc::StructInvisible(vec![
386        (0, tlv::TlvItemValueEnc::UInt8(image_codec.to_u8())).into(),
387        (1, tlv::TlvItemValueEnc::UInt16(max_frame_rate)).into(),
388        (2, tlv::TlvItemValueEnc::StructInvisible(min_resolution_fields)).into(),
389        (3, tlv::TlvItemValueEnc::StructInvisible(max_resolution_fields)).into(),
390        (4, tlv::TlvItemValueEnc::UInt8(quality)).into(),
391        (5, tlv::TlvItemValueEnc::Bool(watermark_enabled)).into(),
392        (6, tlv::TlvItemValueEnc::Bool(osd_enabled)).into(),
393        ]),
394    };
395    Ok(tlv.encode()?)
396}
397
398/// Encode SnapshotStreamModify command (0x09)
399pub fn encode_snapshot_stream_modify(snapshot_stream_id: u8, watermark_enabled: bool, osd_enabled: bool) -> anyhow::Result<Vec<u8>> {
400    let tlv = tlv::TlvItemEnc {
401        tag: 0,
402        value: tlv::TlvItemValueEnc::StructInvisible(vec![
403        (0, tlv::TlvItemValueEnc::UInt8(snapshot_stream_id)).into(),
404        (1, tlv::TlvItemValueEnc::Bool(watermark_enabled)).into(),
405        (2, tlv::TlvItemValueEnc::Bool(osd_enabled)).into(),
406        ]),
407    };
408    Ok(tlv.encode()?)
409}
410
411/// Encode SnapshotStreamDeallocate command (0x0A)
412pub fn encode_snapshot_stream_deallocate(snapshot_stream_id: u8) -> anyhow::Result<Vec<u8>> {
413    let tlv = tlv::TlvItemEnc {
414        tag: 0,
415        value: tlv::TlvItemValueEnc::StructInvisible(vec![
416        (0, tlv::TlvItemValueEnc::UInt8(snapshot_stream_id)).into(),
417        ]),
418    };
419    Ok(tlv.encode()?)
420}
421
422/// Encode SetStreamPriorities command (0x0B)
423pub fn encode_set_stream_priorities(stream_priorities: Vec<u8>) -> anyhow::Result<Vec<u8>> {
424    let tlv = tlv::TlvItemEnc {
425        tag: 0,
426        value: tlv::TlvItemValueEnc::StructInvisible(vec![
427        (0, tlv::TlvItemValueEnc::StructAnon(stream_priorities.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
428        ]),
429    };
430    Ok(tlv.encode()?)
431}
432
433/// Encode CaptureSnapshot command (0x0C)
434pub fn encode_capture_snapshot(snapshot_stream_id: Option<u8>, requested_resolution: VideoResolution) -> anyhow::Result<Vec<u8>> {
435            // Encode struct VideoResolutionStruct
436            let mut requested_resolution_fields = Vec::new();
437            if let Some(x) = requested_resolution.width { requested_resolution_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
438            if let Some(x) = requested_resolution.height { requested_resolution_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
439    let tlv = tlv::TlvItemEnc {
440        tag: 0,
441        value: tlv::TlvItemValueEnc::StructInvisible(vec![
442        (0, tlv::TlvItemValueEnc::UInt8(snapshot_stream_id.unwrap_or(0))).into(),
443        (1, tlv::TlvItemValueEnc::StructInvisible(requested_resolution_fields)).into(),
444        ]),
445    };
446    Ok(tlv.encode()?)
447}
448
449// Attribute decoders
450
451/// Decode MaxConcurrentEncoders attribute (0x0000)
452pub fn decode_max_concurrent_encoders(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
453    if let tlv::TlvItemValue::Int(v) = inp {
454        Ok(*v as u8)
455    } else {
456        Err(anyhow::anyhow!("Expected UInt8"))
457    }
458}
459
460/// Decode MaxEncodedPixelRate attribute (0x0001)
461pub fn decode_max_encoded_pixel_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
462    if let tlv::TlvItemValue::Int(v) = inp {
463        Ok(*v as u32)
464    } else {
465        Err(anyhow::anyhow!("Expected UInt32"))
466    }
467}
468
469/// Decode VideoSensorParams attribute (0x0002)
470pub fn decode_video_sensor_params(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoSensorParams> {
471    if let tlv::TlvItemValue::List(_fields) = inp {
472        // Struct with fields
473        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
474        Ok(VideoSensorParams {
475                sensor_width: item.get_int(&[0]).map(|v| v as u16),
476                sensor_height: item.get_int(&[1]).map(|v| v as u16),
477                max_fps: item.get_int(&[2]).map(|v| v as u16),
478                max_hdrfps: item.get_int(&[3]).map(|v| v as u16),
479        })
480    } else {
481        Err(anyhow::anyhow!("Expected struct fields"))
482    }
483}
484
485/// Decode NightVisionUsesInfrared attribute (0x0003)
486pub fn decode_night_vision_uses_infrared(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
487    if let tlv::TlvItemValue::Bool(v) = inp {
488        Ok(*v)
489    } else {
490        Err(anyhow::anyhow!("Expected Bool"))
491    }
492}
493
494/// Decode MinViewportResolution attribute (0x0004)
495pub fn decode_min_viewport_resolution(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoResolution> {
496    if let tlv::TlvItemValue::List(_fields) = inp {
497        // Struct with fields
498        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
499        Ok(VideoResolution {
500                width: item.get_int(&[0]).map(|v| v as u16),
501                height: item.get_int(&[1]).map(|v| v as u16),
502        })
503    } else {
504        Err(anyhow::anyhow!("Expected struct fields"))
505    }
506}
507
508/// Decode RateDistortionTradeOffPoints attribute (0x0005)
509pub fn decode_rate_distortion_trade_off_points(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<RateDistortionTradeOffPoints>> {
510    let mut res = Vec::new();
511    if let tlv::TlvItemValue::List(v) = inp {
512        for item in v {
513            res.push(RateDistortionTradeOffPoints {
514                codec: item.get_int(&[0]).and_then(|v| VideoCodec::from_u8(v as u8)),
515                resolution: {
516                    if let Some(nested_tlv) = item.get(&[1]) {
517                        if let tlv::TlvItemValue::List(_) = nested_tlv {
518                            let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
519                            Some(VideoResolution {
520                width: nested_item.get_int(&[0]).map(|v| v as u16),
521                height: nested_item.get_int(&[1]).map(|v| v as u16),
522                            })
523                        } else {
524                            None
525                        }
526                    } else {
527                        None
528                    }
529                },
530                min_bit_rate: item.get_int(&[2]).map(|v| v as u32),
531            });
532        }
533    }
534    Ok(res)
535}
536
537/// Decode MaxContentBufferSize attribute (0x0006)
538pub fn decode_max_content_buffer_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
539    if let tlv::TlvItemValue::Int(v) = inp {
540        Ok(*v as u32)
541    } else {
542        Err(anyhow::anyhow!("Expected UInt32"))
543    }
544}
545
546/// Decode MicrophoneCapabilities attribute (0x0007)
547pub fn decode_microphone_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioCapabilities> {
548    if let tlv::TlvItemValue::List(_fields) = inp {
549        // Struct with fields
550        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
551        Ok(AudioCapabilities {
552                max_number_of_channels: item.get_int(&[0]).map(|v| v as u8),
553                supported_codecs: {
554                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
555                        let items: Vec<AudioCodec> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { AudioCodec::from_u8(*v as u8) } else { None } }).collect();
556                        Some(items)
557                    } else {
558                        None
559                    }
560                },
561                supported_sample_rates: {
562                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
563                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
564                        Some(items)
565                    } else {
566                        None
567                    }
568                },
569                supported_bit_depths: {
570                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
571                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
572                        Some(items)
573                    } else {
574                        None
575                    }
576                },
577        })
578    } else {
579        Err(anyhow::anyhow!("Expected struct fields"))
580    }
581}
582
583/// Decode SpeakerCapabilities attribute (0x0008)
584pub fn decode_speaker_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioCapabilities> {
585    if let tlv::TlvItemValue::List(_fields) = inp {
586        // Struct with fields
587        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
588        Ok(AudioCapabilities {
589                max_number_of_channels: item.get_int(&[0]).map(|v| v as u8),
590                supported_codecs: {
591                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
592                        let items: Vec<AudioCodec> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { AudioCodec::from_u8(*v as u8) } else { None } }).collect();
593                        Some(items)
594                    } else {
595                        None
596                    }
597                },
598                supported_sample_rates: {
599                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
600                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
601                        Some(items)
602                    } else {
603                        None
604                    }
605                },
606                supported_bit_depths: {
607                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
608                        let items: Vec<u8> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u8) } else { None } }).collect();
609                        Some(items)
610                    } else {
611                        None
612                    }
613                },
614        })
615    } else {
616        Err(anyhow::anyhow!("Expected struct fields"))
617    }
618}
619
620/// Decode TwoWayTalkSupport attribute (0x0009)
621pub fn decode_two_way_talk_support(inp: &tlv::TlvItemValue) -> anyhow::Result<TwoWayTalkSupportType> {
622    if let tlv::TlvItemValue::Int(v) = inp {
623        TwoWayTalkSupportType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
624    } else {
625        Err(anyhow::anyhow!("Expected Integer"))
626    }
627}
628
629/// Decode SnapshotCapabilities attribute (0x000A)
630pub fn decode_snapshot_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SnapshotCapabilities>> {
631    let mut res = Vec::new();
632    if let tlv::TlvItemValue::List(v) = inp {
633        for item in v {
634            res.push(SnapshotCapabilities {
635                resolution: {
636                    if let Some(nested_tlv) = item.get(&[0]) {
637                        if let tlv::TlvItemValue::List(_) = nested_tlv {
638                            let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
639                            Some(VideoResolution {
640                width: nested_item.get_int(&[0]).map(|v| v as u16),
641                height: nested_item.get_int(&[1]).map(|v| v as u16),
642                            })
643                        } else {
644                            None
645                        }
646                    } else {
647                        None
648                    }
649                },
650                max_frame_rate: item.get_int(&[1]).map(|v| v as u16),
651                image_codec: item.get_int(&[2]).and_then(|v| ImageCodec::from_u8(v as u8)),
652                requires_encoded_pixels: item.get_bool(&[3]),
653                requires_hardware_encoder: item.get_bool(&[4]),
654            });
655        }
656    }
657    Ok(res)
658}
659
660/// Decode MaxNetworkBandwidth attribute (0x000B)
661pub fn decode_max_network_bandwidth(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
662    if let tlv::TlvItemValue::Int(v) = inp {
663        Ok(*v as u32)
664    } else {
665        Err(anyhow::anyhow!("Expected UInt32"))
666    }
667}
668
669/// Decode CurrentFrameRate attribute (0x000C)
670pub fn decode_current_frame_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
671    if let tlv::TlvItemValue::Int(v) = inp {
672        Ok(*v as u16)
673    } else {
674        Err(anyhow::anyhow!("Expected UInt16"))
675    }
676}
677
678/// Decode HDRModeEnabled attribute (0x000D)
679pub fn decode_hdr_mode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
680    if let tlv::TlvItemValue::Bool(v) = inp {
681        Ok(*v)
682    } else {
683        Err(anyhow::anyhow!("Expected Bool"))
684    }
685}
686
687/// Decode SupportedStreamUsages attribute (0x000E)
688pub fn decode_supported_stream_usages(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
689    let mut res = Vec::new();
690    if let tlv::TlvItemValue::List(v) = inp {
691        for item in v {
692            if let tlv::TlvItemValue::Int(i) = &item.value {
693                res.push(*i as u8);
694            }
695        }
696    }
697    Ok(res)
698}
699
700/// Decode AllocatedVideoStreams attribute (0x000F)
701pub fn decode_allocated_video_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<VideoStream>> {
702    let mut res = Vec::new();
703    if let tlv::TlvItemValue::List(v) = inp {
704        for item in v {
705            res.push(VideoStream {
706                video_stream_id: item.get_int(&[0]).map(|v| v as u8),
707                stream_usage: item.get_int(&[1]).map(|v| v as u8),
708                video_codec: item.get_int(&[2]).and_then(|v| VideoCodec::from_u8(v as u8)),
709                min_frame_rate: item.get_int(&[3]).map(|v| v as u16),
710                max_frame_rate: item.get_int(&[4]).map(|v| v as u16),
711                min_resolution: {
712                    if let Some(nested_tlv) = item.get(&[5]) {
713                        if let tlv::TlvItemValue::List(_) = nested_tlv {
714                            let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
715                            Some(VideoResolution {
716                width: nested_item.get_int(&[0]).map(|v| v as u16),
717                height: nested_item.get_int(&[1]).map(|v| v as u16),
718                            })
719                        } else {
720                            None
721                        }
722                    } else {
723                        None
724                    }
725                },
726                max_resolution: {
727                    if let Some(nested_tlv) = item.get(&[6]) {
728                        if let tlv::TlvItemValue::List(_) = nested_tlv {
729                            let nested_item = tlv::TlvItem { tag: 6, value: nested_tlv.clone() };
730                            Some(VideoResolution {
731                width: nested_item.get_int(&[0]).map(|v| v as u16),
732                height: nested_item.get_int(&[1]).map(|v| v as u16),
733                            })
734                        } else {
735                            None
736                        }
737                    } else {
738                        None
739                    }
740                },
741                min_bit_rate: item.get_int(&[7]).map(|v| v as u32),
742                max_bit_rate: item.get_int(&[8]).map(|v| v as u32),
743                key_frame_interval: item.get_int(&[9]).map(|v| v as u16),
744                watermark_enabled: item.get_bool(&[10]),
745                osd_enabled: item.get_bool(&[11]),
746                reference_count: item.get_int(&[12]).map(|v| v as u8),
747            });
748        }
749    }
750    Ok(res)
751}
752
753/// Decode AllocatedAudioStreams attribute (0x0010)
754pub fn decode_allocated_audio_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AudioStream>> {
755    let mut res = Vec::new();
756    if let tlv::TlvItemValue::List(v) = inp {
757        for item in v {
758            res.push(AudioStream {
759                audio_stream_id: item.get_int(&[0]).map(|v| v as u8),
760                stream_usage: item.get_int(&[1]).map(|v| v as u8),
761                audio_codec: item.get_int(&[2]).and_then(|v| AudioCodec::from_u8(v as u8)),
762                channel_count: item.get_int(&[3]).map(|v| v as u8),
763                sample_rate: item.get_int(&[4]).map(|v| v as u32),
764                bit_rate: item.get_int(&[5]).map(|v| v as u32),
765                bit_depth: item.get_int(&[6]).map(|v| v as u8),
766                reference_count: item.get_int(&[7]).map(|v| v as u8),
767            });
768        }
769    }
770    Ok(res)
771}
772
773/// Decode AllocatedSnapshotStreams attribute (0x0011)
774pub fn decode_allocated_snapshot_streams(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SnapshotStream>> {
775    let mut res = Vec::new();
776    if let tlv::TlvItemValue::List(v) = inp {
777        for item in v {
778            res.push(SnapshotStream {
779                snapshot_stream_id: item.get_int(&[0]).map(|v| v as u8),
780                image_codec: item.get_int(&[1]).and_then(|v| ImageCodec::from_u8(v as u8)),
781                frame_rate: item.get_int(&[2]).map(|v| v as u16),
782                min_resolution: {
783                    if let Some(nested_tlv) = item.get(&[3]) {
784                        if let tlv::TlvItemValue::List(_) = nested_tlv {
785                            let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
786                            Some(VideoResolution {
787                width: nested_item.get_int(&[0]).map(|v| v as u16),
788                height: nested_item.get_int(&[1]).map(|v| v as u16),
789                            })
790                        } else {
791                            None
792                        }
793                    } else {
794                        None
795                    }
796                },
797                max_resolution: {
798                    if let Some(nested_tlv) = item.get(&[4]) {
799                        if let tlv::TlvItemValue::List(_) = nested_tlv {
800                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
801                            Some(VideoResolution {
802                width: nested_item.get_int(&[0]).map(|v| v as u16),
803                height: nested_item.get_int(&[1]).map(|v| v as u16),
804                            })
805                        } else {
806                            None
807                        }
808                    } else {
809                        None
810                    }
811                },
812                quality: item.get_int(&[5]).map(|v| v as u8),
813                reference_count: item.get_int(&[6]).map(|v| v as u8),
814                encoded_pixels: item.get_bool(&[7]),
815                hardware_encoder: item.get_bool(&[8]),
816                watermark_enabled: item.get_bool(&[9]),
817                osd_enabled: item.get_bool(&[10]),
818            });
819        }
820    }
821    Ok(res)
822}
823
824/// Decode StreamUsagePriorities attribute (0x0012)
825pub fn decode_stream_usage_priorities(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
826    let mut res = Vec::new();
827    if let tlv::TlvItemValue::List(v) = inp {
828        for item in v {
829            if let tlv::TlvItemValue::Int(i) = &item.value {
830                res.push(*i as u8);
831            }
832        }
833    }
834    Ok(res)
835}
836
837/// Decode SoftRecordingPrivacyModeEnabled attribute (0x0013)
838pub fn decode_soft_recording_privacy_mode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
839    if let tlv::TlvItemValue::Bool(v) = inp {
840        Ok(*v)
841    } else {
842        Err(anyhow::anyhow!("Expected Bool"))
843    }
844}
845
846/// Decode SoftLivestreamPrivacyModeEnabled attribute (0x0014)
847pub fn decode_soft_livestream_privacy_mode_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
848    if let tlv::TlvItemValue::Bool(v) = inp {
849        Ok(*v)
850    } else {
851        Err(anyhow::anyhow!("Expected Bool"))
852    }
853}
854
855/// Decode HardPrivacyModeOn attribute (0x0015)
856pub fn decode_hard_privacy_mode_on(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
857    if let tlv::TlvItemValue::Bool(v) = inp {
858        Ok(*v)
859    } else {
860        Err(anyhow::anyhow!("Expected Bool"))
861    }
862}
863
864/// Decode NightVision attribute (0x0016)
865pub fn decode_night_vision(inp: &tlv::TlvItemValue) -> anyhow::Result<TriStateAuto> {
866    if let tlv::TlvItemValue::Int(v) = inp {
867        TriStateAuto::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
868    } else {
869        Err(anyhow::anyhow!("Expected Integer"))
870    }
871}
872
873/// Decode NightVisionIllum attribute (0x0017)
874pub fn decode_night_vision_illum(inp: &tlv::TlvItemValue) -> anyhow::Result<TriStateAuto> {
875    if let tlv::TlvItemValue::Int(v) = inp {
876        TriStateAuto::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
877    } else {
878        Err(anyhow::anyhow!("Expected Integer"))
879    }
880}
881
882/// Decode Viewport attribute (0x0018)
883pub fn decode_viewport(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
884    if let tlv::TlvItemValue::Int(v) = inp {
885        Ok(*v as u8)
886    } else {
887        Err(anyhow::anyhow!("Expected UInt8"))
888    }
889}
890
891/// Decode SpeakerMuted attribute (0x0019)
892pub fn decode_speaker_muted(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
893    if let tlv::TlvItemValue::Bool(v) = inp {
894        Ok(*v)
895    } else {
896        Err(anyhow::anyhow!("Expected Bool"))
897    }
898}
899
900/// Decode SpeakerVolumeLevel attribute (0x001A)
901pub fn decode_speaker_volume_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
902    if let tlv::TlvItemValue::Int(v) = inp {
903        Ok(*v as u8)
904    } else {
905        Err(anyhow::anyhow!("Expected UInt8"))
906    }
907}
908
909/// Decode SpeakerMaxLevel attribute (0x001B)
910pub fn decode_speaker_max_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
911    if let tlv::TlvItemValue::Int(v) = inp {
912        Ok(*v as u8)
913    } else {
914        Err(anyhow::anyhow!("Expected UInt8"))
915    }
916}
917
918/// Decode SpeakerMinLevel attribute (0x001C)
919pub fn decode_speaker_min_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
920    if let tlv::TlvItemValue::Int(v) = inp {
921        Ok(*v as u8)
922    } else {
923        Err(anyhow::anyhow!("Expected UInt8"))
924    }
925}
926
927/// Decode MicrophoneMuted attribute (0x001D)
928pub fn decode_microphone_muted(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
929    if let tlv::TlvItemValue::Bool(v) = inp {
930        Ok(*v)
931    } else {
932        Err(anyhow::anyhow!("Expected Bool"))
933    }
934}
935
936/// Decode MicrophoneVolumeLevel attribute (0x001E)
937pub fn decode_microphone_volume_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
938    if let tlv::TlvItemValue::Int(v) = inp {
939        Ok(*v as u8)
940    } else {
941        Err(anyhow::anyhow!("Expected UInt8"))
942    }
943}
944
945/// Decode MicrophoneMaxLevel attribute (0x001F)
946pub fn decode_microphone_max_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
947    if let tlv::TlvItemValue::Int(v) = inp {
948        Ok(*v as u8)
949    } else {
950        Err(anyhow::anyhow!("Expected UInt8"))
951    }
952}
953
954/// Decode MicrophoneMinLevel attribute (0x0020)
955pub fn decode_microphone_min_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
956    if let tlv::TlvItemValue::Int(v) = inp {
957        Ok(*v as u8)
958    } else {
959        Err(anyhow::anyhow!("Expected UInt8"))
960    }
961}
962
963/// Decode MicrophoneAGCEnabled attribute (0x0021)
964pub fn decode_microphone_agc_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
965    if let tlv::TlvItemValue::Bool(v) = inp {
966        Ok(*v)
967    } else {
968        Err(anyhow::anyhow!("Expected Bool"))
969    }
970}
971
972/// Decode ImageRotation attribute (0x0022)
973pub fn decode_image_rotation(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
974    if let tlv::TlvItemValue::Int(v) = inp {
975        Ok(*v as u16)
976    } else {
977        Err(anyhow::anyhow!("Expected UInt16"))
978    }
979}
980
981/// Decode ImageFlipHorizontal attribute (0x0023)
982pub fn decode_image_flip_horizontal(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
983    if let tlv::TlvItemValue::Bool(v) = inp {
984        Ok(*v)
985    } else {
986        Err(anyhow::anyhow!("Expected Bool"))
987    }
988}
989
990/// Decode ImageFlipVertical attribute (0x0024)
991pub fn decode_image_flip_vertical(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
992    if let tlv::TlvItemValue::Bool(v) = inp {
993        Ok(*v)
994    } else {
995        Err(anyhow::anyhow!("Expected Bool"))
996    }
997}
998
999/// Decode LocalVideoRecordingEnabled attribute (0x0025)
1000pub fn decode_local_video_recording_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1001    if let tlv::TlvItemValue::Bool(v) = inp {
1002        Ok(*v)
1003    } else {
1004        Err(anyhow::anyhow!("Expected Bool"))
1005    }
1006}
1007
1008/// Decode LocalSnapshotRecordingEnabled attribute (0x0026)
1009pub fn decode_local_snapshot_recording_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1010    if let tlv::TlvItemValue::Bool(v) = inp {
1011        Ok(*v)
1012    } else {
1013        Err(anyhow::anyhow!("Expected Bool"))
1014    }
1015}
1016
1017/// Decode StatusLightEnabled attribute (0x0027)
1018pub fn decode_status_light_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1019    if let tlv::TlvItemValue::Bool(v) = inp {
1020        Ok(*v)
1021    } else {
1022        Err(anyhow::anyhow!("Expected Bool"))
1023    }
1024}
1025
1026/// Decode StatusLightBrightness attribute (0x0028)
1027pub fn decode_status_light_brightness(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1028    if let tlv::TlvItemValue::Int(v) = inp {
1029        Ok(*v as u8)
1030    } else {
1031        Err(anyhow::anyhow!("Expected UInt8"))
1032    }
1033}
1034
1035
1036// JSON dispatcher function
1037
1038/// Decode attribute value and return as JSON string
1039///
1040/// # Parameters
1041/// * `cluster_id` - The cluster identifier
1042/// * `attribute_id` - The attribute identifier
1043/// * `tlv_value` - The TLV value to decode
1044///
1045/// # Returns
1046/// JSON string representation of the decoded value or error
1047pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1048    // Verify this is the correct cluster
1049    if cluster_id != 0x0551 {
1050        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0551, got {}\"}}", cluster_id);
1051    }
1052
1053    match attribute_id {
1054        0x0000 => {
1055            match decode_max_concurrent_encoders(tlv_value) {
1056                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1057                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1058            }
1059        }
1060        0x0001 => {
1061            match decode_max_encoded_pixel_rate(tlv_value) {
1062                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1063                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1064            }
1065        }
1066        0x0002 => {
1067            match decode_video_sensor_params(tlv_value) {
1068                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1069                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1070            }
1071        }
1072        0x0003 => {
1073            match decode_night_vision_uses_infrared(tlv_value) {
1074                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1075                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1076            }
1077        }
1078        0x0004 => {
1079            match decode_min_viewport_resolution(tlv_value) {
1080                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1081                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1082            }
1083        }
1084        0x0005 => {
1085            match decode_rate_distortion_trade_off_points(tlv_value) {
1086                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1087                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1088            }
1089        }
1090        0x0006 => {
1091            match decode_max_content_buffer_size(tlv_value) {
1092                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1093                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1094            }
1095        }
1096        0x0007 => {
1097            match decode_microphone_capabilities(tlv_value) {
1098                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1099                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1100            }
1101        }
1102        0x0008 => {
1103            match decode_speaker_capabilities(tlv_value) {
1104                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1105                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1106            }
1107        }
1108        0x0009 => {
1109            match decode_two_way_talk_support(tlv_value) {
1110                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1111                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1112            }
1113        }
1114        0x000A => {
1115            match decode_snapshot_capabilities(tlv_value) {
1116                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1117                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1118            }
1119        }
1120        0x000B => {
1121            match decode_max_network_bandwidth(tlv_value) {
1122                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1123                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1124            }
1125        }
1126        0x000C => {
1127            match decode_current_frame_rate(tlv_value) {
1128                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1129                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1130            }
1131        }
1132        0x000D => {
1133            match decode_hdr_mode_enabled(tlv_value) {
1134                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1135                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1136            }
1137        }
1138        0x000E => {
1139            match decode_supported_stream_usages(tlv_value) {
1140                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1141                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1142            }
1143        }
1144        0x000F => {
1145            match decode_allocated_video_streams(tlv_value) {
1146                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1147                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1148            }
1149        }
1150        0x0010 => {
1151            match decode_allocated_audio_streams(tlv_value) {
1152                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1153                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1154            }
1155        }
1156        0x0011 => {
1157            match decode_allocated_snapshot_streams(tlv_value) {
1158                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1159                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1160            }
1161        }
1162        0x0012 => {
1163            match decode_stream_usage_priorities(tlv_value) {
1164                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1165                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1166            }
1167        }
1168        0x0013 => {
1169            match decode_soft_recording_privacy_mode_enabled(tlv_value) {
1170                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1171                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1172            }
1173        }
1174        0x0014 => {
1175            match decode_soft_livestream_privacy_mode_enabled(tlv_value) {
1176                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1177                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1178            }
1179        }
1180        0x0015 => {
1181            match decode_hard_privacy_mode_on(tlv_value) {
1182                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1183                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1184            }
1185        }
1186        0x0016 => {
1187            match decode_night_vision(tlv_value) {
1188                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1189                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1190            }
1191        }
1192        0x0017 => {
1193            match decode_night_vision_illum(tlv_value) {
1194                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1195                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1196            }
1197        }
1198        0x0018 => {
1199            match decode_viewport(tlv_value) {
1200                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1201                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1202            }
1203        }
1204        0x0019 => {
1205            match decode_speaker_muted(tlv_value) {
1206                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1207                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1208            }
1209        }
1210        0x001A => {
1211            match decode_speaker_volume_level(tlv_value) {
1212                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1213                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1214            }
1215        }
1216        0x001B => {
1217            match decode_speaker_max_level(tlv_value) {
1218                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1219                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1220            }
1221        }
1222        0x001C => {
1223            match decode_speaker_min_level(tlv_value) {
1224                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1225                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1226            }
1227        }
1228        0x001D => {
1229            match decode_microphone_muted(tlv_value) {
1230                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1231                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1232            }
1233        }
1234        0x001E => {
1235            match decode_microphone_volume_level(tlv_value) {
1236                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1237                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1238            }
1239        }
1240        0x001F => {
1241            match decode_microphone_max_level(tlv_value) {
1242                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1243                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1244            }
1245        }
1246        0x0020 => {
1247            match decode_microphone_min_level(tlv_value) {
1248                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1249                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1250            }
1251        }
1252        0x0021 => {
1253            match decode_microphone_agc_enabled(tlv_value) {
1254                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1255                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1256            }
1257        }
1258        0x0022 => {
1259            match decode_image_rotation(tlv_value) {
1260                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1261                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1262            }
1263        }
1264        0x0023 => {
1265            match decode_image_flip_horizontal(tlv_value) {
1266                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1267                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1268            }
1269        }
1270        0x0024 => {
1271            match decode_image_flip_vertical(tlv_value) {
1272                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1273                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1274            }
1275        }
1276        0x0025 => {
1277            match decode_local_video_recording_enabled(tlv_value) {
1278                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1279                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1280            }
1281        }
1282        0x0026 => {
1283            match decode_local_snapshot_recording_enabled(tlv_value) {
1284                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1285                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1286            }
1287        }
1288        0x0027 => {
1289            match decode_status_light_enabled(tlv_value) {
1290                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1291                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1292            }
1293        }
1294        0x0028 => {
1295            match decode_status_light_brightness(tlv_value) {
1296                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1297                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1298            }
1299        }
1300        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
1301    }
1302}
1303
1304/// Get list of all attributes supported by this cluster
1305///
1306/// # Returns
1307/// Vector of tuples containing (attribute_id, attribute_name)
1308pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
1309    vec![
1310        (0x0000, "MaxConcurrentEncoders"),
1311        (0x0001, "MaxEncodedPixelRate"),
1312        (0x0002, "VideoSensorParams"),
1313        (0x0003, "NightVisionUsesInfrared"),
1314        (0x0004, "MinViewportResolution"),
1315        (0x0005, "RateDistortionTradeOffPoints"),
1316        (0x0006, "MaxContentBufferSize"),
1317        (0x0007, "MicrophoneCapabilities"),
1318        (0x0008, "SpeakerCapabilities"),
1319        (0x0009, "TwoWayTalkSupport"),
1320        (0x000A, "SnapshotCapabilities"),
1321        (0x000B, "MaxNetworkBandwidth"),
1322        (0x000C, "CurrentFrameRate"),
1323        (0x000D, "HDRModeEnabled"),
1324        (0x000E, "SupportedStreamUsages"),
1325        (0x000F, "AllocatedVideoStreams"),
1326        (0x0010, "AllocatedAudioStreams"),
1327        (0x0011, "AllocatedSnapshotStreams"),
1328        (0x0012, "StreamUsagePriorities"),
1329        (0x0013, "SoftRecordingPrivacyModeEnabled"),
1330        (0x0014, "SoftLivestreamPrivacyModeEnabled"),
1331        (0x0015, "HardPrivacyModeOn"),
1332        (0x0016, "NightVision"),
1333        (0x0017, "NightVisionIllum"),
1334        (0x0018, "Viewport"),
1335        (0x0019, "SpeakerMuted"),
1336        (0x001A, "SpeakerVolumeLevel"),
1337        (0x001B, "SpeakerMaxLevel"),
1338        (0x001C, "SpeakerMinLevel"),
1339        (0x001D, "MicrophoneMuted"),
1340        (0x001E, "MicrophoneVolumeLevel"),
1341        (0x001F, "MicrophoneMaxLevel"),
1342        (0x0020, "MicrophoneMinLevel"),
1343        (0x0021, "MicrophoneAGCEnabled"),
1344        (0x0022, "ImageRotation"),
1345        (0x0023, "ImageFlipHorizontal"),
1346        (0x0024, "ImageFlipVertical"),
1347        (0x0025, "LocalVideoRecordingEnabled"),
1348        (0x0026, "LocalSnapshotRecordingEnabled"),
1349        (0x0027, "StatusLightEnabled"),
1350        (0x0028, "StatusLightBrightness"),
1351    ]
1352}
1353
1354#[derive(Debug, serde::Serialize)]
1355pub struct AudioStreamAllocateResponse {
1356    pub audio_stream_id: Option<u8>,
1357}
1358
1359#[derive(Debug, serde::Serialize)]
1360pub struct VideoStreamAllocateResponse {
1361    pub video_stream_id: Option<u8>,
1362}
1363
1364#[derive(Debug, serde::Serialize)]
1365pub struct SnapshotStreamAllocateResponse {
1366    pub snapshot_stream_id: Option<u8>,
1367}
1368
1369#[derive(Debug, serde::Serialize)]
1370pub struct CaptureSnapshotResponse {
1371    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
1372    pub data: Option<Vec<u8>>,
1373    pub image_codec: Option<ImageCodec>,
1374    pub resolution: Option<VideoResolution>,
1375}
1376
1377// Command response decoders
1378
1379/// Decode AudioStreamAllocateResponse command response (01)
1380pub fn decode_audio_stream_allocate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioStreamAllocateResponse> {
1381    if let tlv::TlvItemValue::List(_fields) = inp {
1382        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1383        Ok(AudioStreamAllocateResponse {
1384                audio_stream_id: item.get_int(&[0]).map(|v| v as u8),
1385        })
1386    } else {
1387        Err(anyhow::anyhow!("Expected struct fields"))
1388    }
1389}
1390
1391/// Decode VideoStreamAllocateResponse command response (04)
1392pub fn decode_video_stream_allocate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoStreamAllocateResponse> {
1393    if let tlv::TlvItemValue::List(_fields) = inp {
1394        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1395        Ok(VideoStreamAllocateResponse {
1396                video_stream_id: item.get_int(&[0]).map(|v| v as u8),
1397        })
1398    } else {
1399        Err(anyhow::anyhow!("Expected struct fields"))
1400    }
1401}
1402
1403/// Decode SnapshotStreamAllocateResponse command response (08)
1404pub fn decode_snapshot_stream_allocate_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SnapshotStreamAllocateResponse> {
1405    if let tlv::TlvItemValue::List(_fields) = inp {
1406        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1407        Ok(SnapshotStreamAllocateResponse {
1408                snapshot_stream_id: item.get_int(&[0]).map(|v| v as u8),
1409        })
1410    } else {
1411        Err(anyhow::anyhow!("Expected struct fields"))
1412    }
1413}
1414
1415/// Decode CaptureSnapshotResponse command response (0D)
1416pub fn decode_capture_snapshot_response(inp: &tlv::TlvItemValue) -> anyhow::Result<CaptureSnapshotResponse> {
1417    if let tlv::TlvItemValue::List(_fields) = inp {
1418        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1419        Ok(CaptureSnapshotResponse {
1420                data: item.get_octet_string_owned(&[0]),
1421                image_codec: item.get_int(&[1]).and_then(|v| ImageCodec::from_u8(v as u8)),
1422                resolution: {
1423                    if let Some(nested_tlv) = item.get(&[2]) {
1424                        if let tlv::TlvItemValue::List(_) = nested_tlv {
1425                            let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
1426                            Some(VideoResolution {
1427                width: nested_item.get_int(&[0]).map(|v| v as u16),
1428                height: nested_item.get_int(&[1]).map(|v| v as u16),
1429                            })
1430                        } else {
1431                            None
1432                        }
1433                    } else {
1434                        None
1435                    }
1436                },
1437        })
1438    } else {
1439        Err(anyhow::anyhow!("Expected struct fields"))
1440    }
1441}
1442