1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum AudioCodec {
19 Opus = 0,
21 AacLc = 1,
23}
24
25impl AudioCodec {
26 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 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 = 0,
52 Heic = 1,
54}
55
56impl ImageCodec {
57 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 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 = 0,
83 On = 1,
85 Auto = 2,
87}
88
89impl TriStateAuto {
90 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 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 Notsupported = 0,
117 Halfduplex = 1,
119 Fullduplex = 2,
121}
122
123impl TwoWayTalkSupportType {
124 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 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 H264 = 0,
151 Hevc = 1,
153 Vvc = 2,
155 Av1 = 3,
157}
158
159impl VideoCodec {
160 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 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#[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
276pub 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
294pub 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
305pub 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
320pub fn encode_video_stream_allocate(params: VideoStreamAllocateParams) -> anyhow::Result<Vec<u8>> {
322 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 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
349pub 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
362pub 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
373pub 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 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 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
398pub 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
411pub 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
422pub 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
433pub fn encode_capture_snapshot(snapshot_stream_id: Option<u8>, requested_resolution: VideoResolution) -> anyhow::Result<Vec<u8>> {
435 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
449pub 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
460pub 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
469pub fn decode_video_sensor_params(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoSensorParams> {
471 if let tlv::TlvItemValue::List(_fields) = inp {
472 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
485pub 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
494pub fn decode_min_viewport_resolution(inp: &tlv::TlvItemValue) -> anyhow::Result<VideoResolution> {
496 if let tlv::TlvItemValue::List(_fields) = inp {
497 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
508pub 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
537pub 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
546pub fn decode_microphone_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioCapabilities> {
548 if let tlv::TlvItemValue::List(_fields) = inp {
549 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
583pub fn decode_speaker_capabilities(inp: &tlv::TlvItemValue) -> anyhow::Result<AudioCapabilities> {
585 if let tlv::TlvItemValue::List(_fields) = inp {
586 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
620pub 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
629pub 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
660pub 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
669pub 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
678pub 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
687pub 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
700pub 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
753pub 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
773pub 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
824pub 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
837pub 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
846pub 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
855pub 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
864pub 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
873pub 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
882pub 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
891pub 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
900pub 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
909pub 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
918pub 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
927pub 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
936pub 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
945pub 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
954pub 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
963pub 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
972pub 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
981pub 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
990pub 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
999pub 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
1008pub 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
1017pub 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
1026pub 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
1036pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1048 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
1304pub 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
1377pub 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
1391pub 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
1403pub 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
1415pub 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