1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum CMAFInterface {
21 Interface1 = 0,
23 Interface2dash = 1,
25 Interface2hls = 2,
27}
28
29impl CMAFInterface {
30 pub fn from_u8(value: u8) -> Option<Self> {
32 match value {
33 0 => Some(CMAFInterface::Interface1),
34 1 => Some(CMAFInterface::Interface2dash),
35 2 => Some(CMAFInterface::Interface2hls),
36 _ => None,
37 }
38 }
39
40 pub fn to_u8(self) -> u8 {
42 self as u8
43 }
44}
45
46impl From<CMAFInterface> for u8 {
47 fn from(val: CMAFInterface) -> Self {
48 val as u8
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53#[repr(u8)]
54pub enum ContainerFormat {
55 Cmaf = 0,
57}
58
59impl ContainerFormat {
60 pub fn from_u8(value: u8) -> Option<Self> {
62 match value {
63 0 => Some(ContainerFormat::Cmaf),
64 _ => None,
65 }
66 }
67
68 pub fn to_u8(self) -> u8 {
70 self as u8
71 }
72}
73
74impl From<ContainerFormat> for u8 {
75 fn from(val: ContainerFormat) -> Self {
76 val as u8
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
81#[repr(u8)]
82pub enum IngestMethods {
83 Cmafingest = 0,
85}
86
87impl IngestMethods {
88 pub fn from_u8(value: u8) -> Option<Self> {
90 match value {
91 0 => Some(IngestMethods::Cmafingest),
92 _ => None,
93 }
94 }
95
96 pub fn to_u8(self) -> u8 {
98 self as u8
99 }
100}
101
102impl From<IngestMethods> for u8 {
103 fn from(val: IngestMethods) -> Self {
104 val as u8
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
109#[repr(u8)]
110pub enum StatusCode {
111 Invalidtlsendpoint = 2,
113 Invalidstream = 3,
115 Invalidurl = 4,
117 Invalidzone = 5,
119 Invalidcombination = 6,
121 Invalidtriggertype = 7,
123 Invalidtransportstatus = 8,
125 Invalidoptions = 9,
127 Invalidstreamusage = 10,
129 Invalidtime = 11,
131 Invalidprerolllength = 12,
133 Duplicatestreamvalues = 13,
135}
136
137impl StatusCode {
138 pub fn from_u8(value: u8) -> Option<Self> {
140 match value {
141 2 => Some(StatusCode::Invalidtlsendpoint),
142 3 => Some(StatusCode::Invalidstream),
143 4 => Some(StatusCode::Invalidurl),
144 5 => Some(StatusCode::Invalidzone),
145 6 => Some(StatusCode::Invalidcombination),
146 7 => Some(StatusCode::Invalidtriggertype),
147 8 => Some(StatusCode::Invalidtransportstatus),
148 9 => Some(StatusCode::Invalidoptions),
149 10 => Some(StatusCode::Invalidstreamusage),
150 11 => Some(StatusCode::Invalidtime),
151 12 => Some(StatusCode::Invalidprerolllength),
152 13 => Some(StatusCode::Duplicatestreamvalues),
153 _ => None,
154 }
155 }
156
157 pub fn to_u8(self) -> u8 {
159 self as u8
160 }
161}
162
163impl From<StatusCode> for u8 {
164 fn from(val: StatusCode) -> Self {
165 val as u8
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
170#[repr(u8)]
171pub enum TransportStatus {
172 Active = 0,
174 Inactive = 1,
176}
177
178impl TransportStatus {
179 pub fn from_u8(value: u8) -> Option<Self> {
181 match value {
182 0 => Some(TransportStatus::Active),
183 1 => Some(TransportStatus::Inactive),
184 _ => None,
185 }
186 }
187
188 pub fn to_u8(self) -> u8 {
190 self as u8
191 }
192}
193
194impl From<TransportStatus> for u8 {
195 fn from(val: TransportStatus) -> Self {
196 val as u8
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
201#[repr(u8)]
202pub enum TransportTriggerType {
203 Command = 0,
205 Motion = 1,
207 Continuous = 2,
209}
210
211impl TransportTriggerType {
212 pub fn from_u8(value: u8) -> Option<Self> {
214 match value {
215 0 => Some(TransportTriggerType::Command),
216 1 => Some(TransportTriggerType::Motion),
217 2 => Some(TransportTriggerType::Continuous),
218 _ => None,
219 }
220 }
221
222 pub fn to_u8(self) -> u8 {
224 self as u8
225 }
226}
227
228impl From<TransportTriggerType> for u8 {
229 fn from(val: TransportTriggerType) -> Self {
230 val as u8
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
235#[repr(u8)]
236pub enum TriggerActivationReason {
237 Userinitiated = 0,
239 Automation = 1,
241 Emergency = 2,
243 Doorbellpressed = 3,
245}
246
247impl TriggerActivationReason {
248 pub fn from_u8(value: u8) -> Option<Self> {
250 match value {
251 0 => Some(TriggerActivationReason::Userinitiated),
252 1 => Some(TriggerActivationReason::Automation),
253 2 => Some(TriggerActivationReason::Emergency),
254 3 => Some(TriggerActivationReason::Doorbellpressed),
255 _ => None,
256 }
257 }
258
259 pub fn to_u8(self) -> u8 {
261 self as u8
262 }
263}
264
265impl From<TriggerActivationReason> for u8 {
266 fn from(val: TriggerActivationReason) -> Self {
267 val as u8
268 }
269}
270
271#[derive(Debug, serde::Serialize)]
274pub struct AudioStream {
275 pub audio_stream_name: Option<String>,
276 pub audio_stream_id: Option<u16>,
277}
278
279#[derive(Debug, serde::Serialize)]
280pub struct CMAFContainerOptions {
281 pub cmaf_interface: Option<CMAFInterface>,
282 pub segment_duration: Option<u16>,
283 pub chunk_duration: Option<u16>,
284 pub session_group: Option<u8>,
285 pub track_name: Option<String>,
286 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
287 pub cenc_key: Option<Vec<u8>>,
288 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
289 pub cenc_key_id: Option<Vec<u8>>,
290 pub metadata_enabled: Option<bool>,
291}
292
293#[derive(Debug, serde::Serialize)]
294pub struct ContainerOptions {
295 pub container_type: Option<ContainerFormat>,
296 pub cmaf_container_options: Option<CMAFContainerOptions>,
297}
298
299#[derive(Debug, serde::Serialize)]
300pub struct SupportedFormat {
301 pub container_format: Option<ContainerFormat>,
302 pub ingest_method: Option<IngestMethods>,
303}
304
305#[derive(Debug, serde::Serialize)]
306pub struct TransportConfiguration {
307 pub connection_id: Option<u16>,
308 pub transport_status: Option<TransportStatus>,
309 pub transport_options: Option<TransportOptions>,
310}
311
312#[derive(Debug, serde::Serialize)]
313pub struct TransportMotionTriggerTimeControl {
314 pub initial_duration: Option<u16>,
315 pub augmentation_duration: Option<u16>,
316 pub max_duration: Option<u32>,
317 pub blind_duration: Option<u16>,
318}
319
320#[derive(Debug, serde::Serialize)]
321pub struct TransportOptions {
322 pub stream_usage: Option<u8>,
323 pub video_stream_id: Option<u16>,
324 pub audio_stream_id: Option<u16>,
325 pub tls_endpoint_id: Option<u16>,
326 pub url: Option<String>,
327 pub trigger_options: Option<TransportTriggerOptions>,
328 pub ingest_method: Option<IngestMethods>,
329 pub container_options: Option<ContainerOptions>,
330 pub expiry_time: Option<u64>,
331 pub video_streams: Option<Vec<VideoStream>>,
332 pub audio_streams: Option<Vec<AudioStream>>,
333}
334
335#[derive(Debug, serde::Serialize)]
336pub struct TransportTriggerOptions {
337 pub trigger_type: Option<TransportTriggerType>,
338 pub motion_zones: Option<Vec<TransportZoneOptions>>,
339 pub motion_sensitivity: Option<u8>,
340 pub motion_time_control: Option<TransportMotionTriggerTimeControl>,
341 pub max_pre_roll_len: Option<u16>,
342}
343
344#[derive(Debug, serde::Serialize)]
345pub struct TransportZoneOptions {
346 pub zone: Option<u16>,
347 pub sensitivity: Option<u8>,
348}
349
350#[derive(Debug, serde::Serialize)]
351pub struct VideoStream {
352 pub video_stream_name: Option<String>,
353 pub video_stream_id: Option<u16>,
354}
355
356pub fn encode_allocate_push_transport(transport_options: TransportOptions) -> anyhow::Result<Vec<u8>> {
360 let mut transport_options_fields = Vec::new();
362 if let Some(x) = transport_options.stream_usage { transport_options_fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
363 if let Some(x) = transport_options.video_stream_id { transport_options_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
364 if let Some(x) = transport_options.audio_stream_id { transport_options_fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
365 if let Some(x) = transport_options.tls_endpoint_id { transport_options_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
366 if let Some(x) = transport_options.url { transport_options_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
367 if let Some(inner) = transport_options.trigger_options {
368 let mut trigger_options_nested_fields = Vec::new();
369 if let Some(x) = inner.trigger_type { trigger_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
370 if let Some(listv) = inner.motion_zones {
371 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
372 let mut nested_fields = Vec::new();
373 if let Some(x) = inner.zone { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
374 if let Some(x) = inner.sensitivity { nested_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
375 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
376 }).collect();
377 trigger_options_nested_fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
378 }
379 if let Some(x) = inner.motion_sensitivity { trigger_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
380 if let Some(inner) = inner.motion_time_control {
381 let mut motion_time_control_nested_fields = Vec::new();
382 if let Some(x) = inner.initial_duration { motion_time_control_nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
383 if let Some(x) = inner.augmentation_duration { motion_time_control_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
384 if let Some(x) = inner.max_duration { motion_time_control_nested_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
385 if let Some(x) = inner.blind_duration { motion_time_control_nested_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
386 trigger_options_nested_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(motion_time_control_nested_fields)).into());
387 }
388 if let Some(x) = inner.max_pre_roll_len { trigger_options_nested_fields.push((4, tlv::TlvItemValueEnc::UInt16(x)).into()); }
389 transport_options_fields.push((5, tlv::TlvItemValueEnc::StructInvisible(trigger_options_nested_fields)).into());
390 }
391 if let Some(x) = transport_options.ingest_method { transport_options_fields.push((6, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
392 if let Some(inner) = transport_options.container_options {
393 let mut container_options_nested_fields = Vec::new();
394 if let Some(x) = inner.container_type { container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
395 if let Some(inner) = inner.cmaf_container_options {
396 let mut cmaf_container_options_nested_fields = Vec::new();
397 if let Some(x) = inner.cmaf_interface { cmaf_container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
398 if let Some(x) = inner.segment_duration { cmaf_container_options_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
399 if let Some(x) = inner.chunk_duration { cmaf_container_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
400 if let Some(x) = inner.session_group { cmaf_container_options_nested_fields.push((3, tlv::TlvItemValueEnc::UInt8(x)).into()); }
401 if let Some(x) = inner.track_name { cmaf_container_options_nested_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
402 if let Some(x) = inner.cenc_key { cmaf_container_options_nested_fields.push((5, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
403 if let Some(x) = inner.cenc_key_id { cmaf_container_options_nested_fields.push((6, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
404 if let Some(x) = inner.metadata_enabled { cmaf_container_options_nested_fields.push((7, tlv::TlvItemValueEnc::Bool(x)).into()); }
405 container_options_nested_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(cmaf_container_options_nested_fields)).into());
406 }
407 transport_options_fields.push((7, tlv::TlvItemValueEnc::StructInvisible(container_options_nested_fields)).into());
408 }
409 if let Some(x) = transport_options.expiry_time { transport_options_fields.push((8, tlv::TlvItemValueEnc::UInt64(x)).into()); }
410 if let Some(listv) = transport_options.video_streams {
411 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
412 let mut nested_fields = Vec::new();
413 if let Some(x) = inner.video_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
414 if let Some(x) = inner.video_stream_id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
415 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
416 }).collect();
417 transport_options_fields.push((9, tlv::TlvItemValueEnc::Array(inner_vec)).into());
418 }
419 if let Some(listv) = transport_options.audio_streams {
420 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
421 let mut nested_fields = Vec::new();
422 if let Some(x) = inner.audio_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
423 if let Some(x) = inner.audio_stream_id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
424 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
425 }).collect();
426 transport_options_fields.push((10, tlv::TlvItemValueEnc::Array(inner_vec)).into());
427 }
428 let tlv = tlv::TlvItemEnc {
429 tag: 0,
430 value: tlv::TlvItemValueEnc::StructInvisible(vec![
431 (0, tlv::TlvItemValueEnc::StructInvisible(transport_options_fields)).into(),
432 ]),
433 };
434 Ok(tlv.encode()?)
435}
436
437pub fn encode_deallocate_push_transport(connection_id: u16) -> anyhow::Result<Vec<u8>> {
439 let tlv = tlv::TlvItemEnc {
440 tag: 0,
441 value: tlv::TlvItemValueEnc::StructInvisible(vec![
442 (0, tlv::TlvItemValueEnc::UInt16(connection_id)).into(),
443 ]),
444 };
445 Ok(tlv.encode()?)
446}
447
448pub fn encode_modify_push_transport(connection_id: u16, transport_options: TransportOptions) -> anyhow::Result<Vec<u8>> {
450 let mut transport_options_fields = Vec::new();
452 if let Some(x) = transport_options.stream_usage { transport_options_fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
453 if let Some(x) = transport_options.video_stream_id { transport_options_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
454 if let Some(x) = transport_options.audio_stream_id { transport_options_fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
455 if let Some(x) = transport_options.tls_endpoint_id { transport_options_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
456 if let Some(x) = transport_options.url { transport_options_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
457 if let Some(inner) = transport_options.trigger_options {
458 let mut trigger_options_nested_fields = Vec::new();
459 if let Some(x) = inner.trigger_type { trigger_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
460 if let Some(listv) = inner.motion_zones {
461 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
462 let mut nested_fields = Vec::new();
463 if let Some(x) = inner.zone { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
464 if let Some(x) = inner.sensitivity { nested_fields.push((1, tlv::TlvItemValueEnc::UInt8(x)).into()); }
465 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
466 }).collect();
467 trigger_options_nested_fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
468 }
469 if let Some(x) = inner.motion_sensitivity { trigger_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt8(x)).into()); }
470 if let Some(inner) = inner.motion_time_control {
471 let mut motion_time_control_nested_fields = Vec::new();
472 if let Some(x) = inner.initial_duration { motion_time_control_nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
473 if let Some(x) = inner.augmentation_duration { motion_time_control_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
474 if let Some(x) = inner.max_duration { motion_time_control_nested_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
475 if let Some(x) = inner.blind_duration { motion_time_control_nested_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
476 trigger_options_nested_fields.push((3, tlv::TlvItemValueEnc::StructInvisible(motion_time_control_nested_fields)).into());
477 }
478 if let Some(x) = inner.max_pre_roll_len { trigger_options_nested_fields.push((4, tlv::TlvItemValueEnc::UInt16(x)).into()); }
479 transport_options_fields.push((5, tlv::TlvItemValueEnc::StructInvisible(trigger_options_nested_fields)).into());
480 }
481 if let Some(x) = transport_options.ingest_method { transport_options_fields.push((6, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
482 if let Some(inner) = transport_options.container_options {
483 let mut container_options_nested_fields = Vec::new();
484 if let Some(x) = inner.container_type { container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
485 if let Some(inner) = inner.cmaf_container_options {
486 let mut cmaf_container_options_nested_fields = Vec::new();
487 if let Some(x) = inner.cmaf_interface { cmaf_container_options_nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
488 if let Some(x) = inner.segment_duration { cmaf_container_options_nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
489 if let Some(x) = inner.chunk_duration { cmaf_container_options_nested_fields.push((2, tlv::TlvItemValueEnc::UInt16(x)).into()); }
490 if let Some(x) = inner.session_group { cmaf_container_options_nested_fields.push((3, tlv::TlvItemValueEnc::UInt8(x)).into()); }
491 if let Some(x) = inner.track_name { cmaf_container_options_nested_fields.push((4, tlv::TlvItemValueEnc::String(x.clone())).into()); }
492 if let Some(x) = inner.cenc_key { cmaf_container_options_nested_fields.push((5, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
493 if let Some(x) = inner.cenc_key_id { cmaf_container_options_nested_fields.push((6, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
494 if let Some(x) = inner.metadata_enabled { cmaf_container_options_nested_fields.push((7, tlv::TlvItemValueEnc::Bool(x)).into()); }
495 container_options_nested_fields.push((1, tlv::TlvItemValueEnc::StructInvisible(cmaf_container_options_nested_fields)).into());
496 }
497 transport_options_fields.push((7, tlv::TlvItemValueEnc::StructInvisible(container_options_nested_fields)).into());
498 }
499 if let Some(x) = transport_options.expiry_time { transport_options_fields.push((8, tlv::TlvItemValueEnc::UInt64(x)).into()); }
500 if let Some(listv) = transport_options.video_streams {
501 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
502 let mut nested_fields = Vec::new();
503 if let Some(x) = inner.video_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
504 if let Some(x) = inner.video_stream_id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
505 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
506 }).collect();
507 transport_options_fields.push((9, tlv::TlvItemValueEnc::Array(inner_vec)).into());
508 }
509 if let Some(listv) = transport_options.audio_streams {
510 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
511 let mut nested_fields = Vec::new();
512 if let Some(x) = inner.audio_stream_name { nested_fields.push((0, tlv::TlvItemValueEnc::String(x.clone())).into()); }
513 if let Some(x) = inner.audio_stream_id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
514 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
515 }).collect();
516 transport_options_fields.push((10, tlv::TlvItemValueEnc::Array(inner_vec)).into());
517 }
518 let tlv = tlv::TlvItemEnc {
519 tag: 0,
520 value: tlv::TlvItemValueEnc::StructInvisible(vec![
521 (0, tlv::TlvItemValueEnc::UInt16(connection_id)).into(),
522 (1, tlv::TlvItemValueEnc::StructInvisible(transport_options_fields)).into(),
523 ]),
524 };
525 Ok(tlv.encode()?)
526}
527
528pub fn encode_set_transport_status(connection_id: Option<u16>, transport_status: TransportStatus) -> anyhow::Result<Vec<u8>> {
530 let tlv = tlv::TlvItemEnc {
531 tag: 0,
532 value: tlv::TlvItemValueEnc::StructInvisible(vec![
533 (0, tlv::TlvItemValueEnc::UInt16(connection_id.unwrap_or(0))).into(),
534 (1, tlv::TlvItemValueEnc::UInt8(transport_status.to_u8())).into(),
535 ]),
536 };
537 Ok(tlv.encode()?)
538}
539
540pub fn encode_manually_trigger_transport(connection_id: u16, activation_reason: TriggerActivationReason, time_control: Option<TransportMotionTriggerTimeControl>, user_defined: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
542 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
543 tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(connection_id)).into());
544 tlv_fields.push((1, tlv::TlvItemValueEnc::UInt8(activation_reason.to_u8())).into());
545 if let Some(time_control) = time_control {
546 let mut time_control_fields = Vec::new();
548 if let Some(x) = time_control.initial_duration { time_control_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
549 if let Some(x) = time_control.augmentation_duration { time_control_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
550 if let Some(x) = time_control.max_duration { time_control_fields.push((2, tlv::TlvItemValueEnc::UInt32(x)).into()); }
551 if let Some(x) = time_control.blind_duration { time_control_fields.push((3, tlv::TlvItemValueEnc::UInt16(x)).into()); }
552 tlv_fields.push((2, tlv::TlvItemValueEnc::StructInvisible(time_control_fields)).into());
553 }
554 if let Some(x) = user_defined { tlv_fields.push((3, tlv::TlvItemValueEnc::OctetString(x)).into()); }
555 let tlv = tlv::TlvItemEnc {
556 tag: 0,
557 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
558 };
559 Ok(tlv.encode()?)
560}
561
562pub fn encode_find_transport(connection_id: Option<u16>) -> anyhow::Result<Vec<u8>> {
564 let tlv = tlv::TlvItemEnc {
565 tag: 0,
566 value: tlv::TlvItemValueEnc::StructInvisible(vec![
567 (0, tlv::TlvItemValueEnc::UInt16(connection_id.unwrap_or(0))).into(),
568 ]),
569 };
570 Ok(tlv.encode()?)
571}
572
573pub fn decode_supported_formats(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<SupportedFormat>> {
577 let mut res = Vec::new();
578 if let tlv::TlvItemValue::List(v) = inp {
579 for item in v {
580 res.push(SupportedFormat {
581 container_format: item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
582 ingest_method: item.get_int(&[1]).and_then(|v| IngestMethods::from_u8(v as u8)),
583 });
584 }
585 }
586 Ok(res)
587}
588
589pub fn decode_current_connections(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TransportConfiguration>> {
591 let mut res = Vec::new();
592 if let tlv::TlvItemValue::List(v) = inp {
593 for item in v {
594 res.push(TransportConfiguration {
595 connection_id: item.get_int(&[0]).map(|v| v as u16),
596 transport_status: item.get_int(&[1]).and_then(|v| TransportStatus::from_u8(v as u8)),
597 transport_options: {
598 if let Some(nested_tlv) = item.get(&[2]) {
599 if let tlv::TlvItemValue::List(_) = nested_tlv {
600 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
601 Some(TransportOptions {
602 stream_usage: nested_item.get_int(&[0]).map(|v| v as u8),
603 video_stream_id: nested_item.get_int(&[1]).map(|v| v as u16),
604 audio_stream_id: nested_item.get_int(&[2]).map(|v| v as u16),
605 tls_endpoint_id: nested_item.get_int(&[3]).map(|v| v as u16),
606 url: nested_item.get_string_owned(&[4]),
607 trigger_options: {
608 if let Some(nested_tlv) = nested_item.get(&[5]) {
609 if let tlv::TlvItemValue::List(_) = nested_tlv {
610 let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
611 Some(TransportTriggerOptions {
612 trigger_type: nested_item.get_int(&[0]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
613 motion_zones: {
614 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
615 let mut items = Vec::new();
616 for list_item in l {
617 items.push(TransportZoneOptions {
618 zone: list_item.get_int(&[0]).map(|v| v as u16),
619 sensitivity: list_item.get_int(&[1]).map(|v| v as u8),
620 });
621 }
622 Some(items)
623 } else {
624 None
625 }
626 },
627 motion_sensitivity: nested_item.get_int(&[2]).map(|v| v as u8),
628 motion_time_control: {
629 if let Some(nested_tlv) = nested_item.get(&[3]) {
630 if let tlv::TlvItemValue::List(_) = nested_tlv {
631 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
632 Some(TransportMotionTriggerTimeControl {
633 initial_duration: nested_item.get_int(&[0]).map(|v| v as u16),
634 augmentation_duration: nested_item.get_int(&[1]).map(|v| v as u16),
635 max_duration: nested_item.get_int(&[2]).map(|v| v as u32),
636 blind_duration: nested_item.get_int(&[3]).map(|v| v as u16),
637 })
638 } else {
639 None
640 }
641 } else {
642 None
643 }
644 },
645 max_pre_roll_len: nested_item.get_int(&[4]).map(|v| v as u16),
646 })
647 } else {
648 None
649 }
650 } else {
651 None
652 }
653 },
654 ingest_method: nested_item.get_int(&[6]).and_then(|v| IngestMethods::from_u8(v as u8)),
655 container_options: {
656 if let Some(nested_tlv) = nested_item.get(&[7]) {
657 if let tlv::TlvItemValue::List(_) = nested_tlv {
658 let nested_item = tlv::TlvItem { tag: 7, value: nested_tlv.clone() };
659 Some(ContainerOptions {
660 container_type: nested_item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
661 cmaf_container_options: {
662 if let Some(nested_tlv) = nested_item.get(&[1]) {
663 if let tlv::TlvItemValue::List(_) = nested_tlv {
664 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
665 Some(CMAFContainerOptions {
666 cmaf_interface: nested_item.get_int(&[0]).and_then(|v| CMAFInterface::from_u8(v as u8)),
667 segment_duration: nested_item.get_int(&[1]).map(|v| v as u16),
668 chunk_duration: nested_item.get_int(&[2]).map(|v| v as u16),
669 session_group: nested_item.get_int(&[3]).map(|v| v as u8),
670 track_name: nested_item.get_string_owned(&[4]),
671 cenc_key: nested_item.get_octet_string_owned(&[5]),
672 cenc_key_id: nested_item.get_octet_string_owned(&[6]),
673 metadata_enabled: nested_item.get_bool(&[7]),
674 })
675 } else {
676 None
677 }
678 } else {
679 None
680 }
681 },
682 })
683 } else {
684 None
685 }
686 } else {
687 None
688 }
689 },
690 expiry_time: nested_item.get_int(&[8]),
691 video_streams: {
692 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[9]) {
693 let mut items = Vec::new();
694 for list_item in l {
695 items.push(VideoStream {
696 video_stream_name: list_item.get_string_owned(&[0]),
697 video_stream_id: list_item.get_int(&[1]).map(|v| v as u16),
698 });
699 }
700 Some(items)
701 } else {
702 None
703 }
704 },
705 audio_streams: {
706 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[10]) {
707 let mut items = Vec::new();
708 for list_item in l {
709 items.push(AudioStream {
710 audio_stream_name: list_item.get_string_owned(&[0]),
711 audio_stream_id: list_item.get_int(&[1]).map(|v| v as u16),
712 });
713 }
714 Some(items)
715 } else {
716 None
717 }
718 },
719 })
720 } else {
721 None
722 }
723 } else {
724 None
725 }
726 },
727 });
728 }
729 }
730 Ok(res)
731}
732
733
734pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
746 if cluster_id != 0x0555 {
748 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0555, got {}\"}}", cluster_id);
749 }
750
751 match attribute_id {
752 0x0000 => {
753 match decode_supported_formats(tlv_value) {
754 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
755 Err(e) => format!("{{\"error\": \"{}\"}}", e),
756 }
757 }
758 0x0001 => {
759 match decode_current_connections(tlv_value) {
760 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
761 Err(e) => format!("{{\"error\": \"{}\"}}", e),
762 }
763 }
764 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
765 }
766}
767
768pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
773 vec![
774 (0x0000, "SupportedFormats"),
775 (0x0001, "CurrentConnections"),
776 ]
777}
778
779pub fn get_command_list() -> Vec<(u32, &'static str)> {
782 vec![
783 (0x00, "AllocatePushTransport"),
784 (0x02, "DeallocatePushTransport"),
785 (0x03, "ModifyPushTransport"),
786 (0x04, "SetTransportStatus"),
787 (0x05, "ManuallyTriggerTransport"),
788 (0x06, "FindTransport"),
789 ]
790}
791
792pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
793 match cmd_id {
794 0x00 => Some("AllocatePushTransport"),
795 0x02 => Some("DeallocatePushTransport"),
796 0x03 => Some("ModifyPushTransport"),
797 0x04 => Some("SetTransportStatus"),
798 0x05 => Some("ManuallyTriggerTransport"),
799 0x06 => Some("FindTransport"),
800 _ => None,
801 }
802}
803
804pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
805 match cmd_id {
806 0x00 => Some(vec![
807 crate::clusters::codec::CommandField { tag: 0, name: "transport_options", kind: crate::clusters::codec::FieldKind::Struct { name: "TransportOptionsStruct" }, optional: false, nullable: false },
808 ]),
809 0x02 => Some(vec![
810 crate::clusters::codec::CommandField { tag: 0, name: "connection_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
811 ]),
812 0x03 => Some(vec![
813 crate::clusters::codec::CommandField { tag: 0, name: "connection_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
814 crate::clusters::codec::CommandField { tag: 1, name: "transport_options", kind: crate::clusters::codec::FieldKind::Struct { name: "TransportOptionsStruct" }, optional: false, nullable: false },
815 ]),
816 0x04 => Some(vec![
817 crate::clusters::codec::CommandField { tag: 0, name: "connection_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
818 crate::clusters::codec::CommandField { tag: 1, name: "transport_status", kind: crate::clusters::codec::FieldKind::Enum { name: "TransportStatus", variants: &[(0, "Active"), (1, "Inactive")] }, optional: false, nullable: false },
819 ]),
820 0x05 => Some(vec![
821 crate::clusters::codec::CommandField { tag: 0, name: "connection_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
822 crate::clusters::codec::CommandField { tag: 1, name: "activation_reason", kind: crate::clusters::codec::FieldKind::Enum { name: "TriggerActivationReason", variants: &[(0, "Userinitiated"), (1, "Automation"), (2, "Emergency"), (3, "Doorbellpressed")] }, optional: false, nullable: false },
823 crate::clusters::codec::CommandField { tag: 2, name: "time_control", kind: crate::clusters::codec::FieldKind::Struct { name: "TransportMotionTriggerTimeControlStruct" }, optional: true, nullable: false },
824 crate::clusters::codec::CommandField { tag: 3, name: "user_defined", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
825 ]),
826 0x06 => Some(vec![
827 crate::clusters::codec::CommandField { tag: 0, name: "connection_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: true },
828 ]),
829 _ => None,
830 }
831}
832
833pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
834 match cmd_id {
835 0x00 => Err(anyhow::anyhow!("command \"AllocatePushTransport\" has complex args: use raw mode")),
836 0x02 => {
837 let connection_id = crate::clusters::codec::json_util::get_u16(args, "connection_id")?;
838 encode_deallocate_push_transport(connection_id)
839 }
840 0x03 => Err(anyhow::anyhow!("command \"ModifyPushTransport\" has complex args: use raw mode")),
841 0x04 => {
842 let connection_id = crate::clusters::codec::json_util::get_opt_u16(args, "connection_id")?;
843 let transport_status = {
844 let n = crate::clusters::codec::json_util::get_u64(args, "transport_status")?;
845 TransportStatus::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid TransportStatus: {}", n))?
846 };
847 encode_set_transport_status(connection_id, transport_status)
848 }
849 0x05 => Err(anyhow::anyhow!("command \"ManuallyTriggerTransport\" has complex args: use raw mode")),
850 0x06 => {
851 let connection_id = crate::clusters::codec::json_util::get_opt_u16(args, "connection_id")?;
852 encode_find_transport(connection_id)
853 }
854 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
855 }
856}
857
858#[derive(Debug, serde::Serialize)]
859pub struct AllocatePushTransportResponse {
860 pub transport_configuration: Option<TransportConfiguration>,
861}
862
863#[derive(Debug, serde::Serialize)]
864pub struct FindTransportResponse {
865 pub transport_configurations: Option<Vec<TransportConfiguration>>,
866}
867
868pub fn decode_allocate_push_transport_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AllocatePushTransportResponse> {
872 if let tlv::TlvItemValue::List(_fields) = inp {
873 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
874 Ok(AllocatePushTransportResponse {
875 transport_configuration: {
876 if let Some(nested_tlv) = item.get(&[0]) {
877 if let tlv::TlvItemValue::List(_) = nested_tlv {
878 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
879 Some(TransportConfiguration {
880 connection_id: nested_item.get_int(&[0]).map(|v| v as u16),
881 transport_status: nested_item.get_int(&[1]).and_then(|v| TransportStatus::from_u8(v as u8)),
882 transport_options: {
883 if let Some(nested_tlv) = nested_item.get(&[2]) {
884 if let tlv::TlvItemValue::List(_) = nested_tlv {
885 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
886 Some(TransportOptions {
887 stream_usage: nested_item.get_int(&[0]).map(|v| v as u8),
888 video_stream_id: nested_item.get_int(&[1]).map(|v| v as u16),
889 audio_stream_id: nested_item.get_int(&[2]).map(|v| v as u16),
890 tls_endpoint_id: nested_item.get_int(&[3]).map(|v| v as u16),
891 url: nested_item.get_string_owned(&[4]),
892 trigger_options: {
893 if let Some(nested_tlv) = nested_item.get(&[5]) {
894 if let tlv::TlvItemValue::List(_) = nested_tlv {
895 let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
896 Some(TransportTriggerOptions {
897 trigger_type: nested_item.get_int(&[0]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
898 motion_zones: {
899 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
900 let mut items = Vec::new();
901 for list_item in l {
902 items.push(TransportZoneOptions {
903 zone: list_item.get_int(&[0]).map(|v| v as u16),
904 sensitivity: list_item.get_int(&[1]).map(|v| v as u8),
905 });
906 }
907 Some(items)
908 } else {
909 None
910 }
911 },
912 motion_sensitivity: nested_item.get_int(&[2]).map(|v| v as u8),
913 motion_time_control: {
914 if let Some(nested_tlv) = nested_item.get(&[3]) {
915 if let tlv::TlvItemValue::List(_) = nested_tlv {
916 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
917 Some(TransportMotionTriggerTimeControl {
918 initial_duration: nested_item.get_int(&[0]).map(|v| v as u16),
919 augmentation_duration: nested_item.get_int(&[1]).map(|v| v as u16),
920 max_duration: nested_item.get_int(&[2]).map(|v| v as u32),
921 blind_duration: nested_item.get_int(&[3]).map(|v| v as u16),
922 })
923 } else {
924 None
925 }
926 } else {
927 None
928 }
929 },
930 max_pre_roll_len: nested_item.get_int(&[4]).map(|v| v as u16),
931 })
932 } else {
933 None
934 }
935 } else {
936 None
937 }
938 },
939 ingest_method: nested_item.get_int(&[6]).and_then(|v| IngestMethods::from_u8(v as u8)),
940 container_options: {
941 if let Some(nested_tlv) = nested_item.get(&[7]) {
942 if let tlv::TlvItemValue::List(_) = nested_tlv {
943 let nested_item = tlv::TlvItem { tag: 7, value: nested_tlv.clone() };
944 Some(ContainerOptions {
945 container_type: nested_item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
946 cmaf_container_options: {
947 if let Some(nested_tlv) = nested_item.get(&[1]) {
948 if let tlv::TlvItemValue::List(_) = nested_tlv {
949 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
950 Some(CMAFContainerOptions {
951 cmaf_interface: nested_item.get_int(&[0]).and_then(|v| CMAFInterface::from_u8(v as u8)),
952 segment_duration: nested_item.get_int(&[1]).map(|v| v as u16),
953 chunk_duration: nested_item.get_int(&[2]).map(|v| v as u16),
954 session_group: nested_item.get_int(&[3]).map(|v| v as u8),
955 track_name: nested_item.get_string_owned(&[4]),
956 cenc_key: nested_item.get_octet_string_owned(&[5]),
957 cenc_key_id: nested_item.get_octet_string_owned(&[6]),
958 metadata_enabled: nested_item.get_bool(&[7]),
959 })
960 } else {
961 None
962 }
963 } else {
964 None
965 }
966 },
967 })
968 } else {
969 None
970 }
971 } else {
972 None
973 }
974 },
975 expiry_time: nested_item.get_int(&[8]),
976 video_streams: {
977 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[9]) {
978 let mut items = Vec::new();
979 for list_item in l {
980 items.push(VideoStream {
981 video_stream_name: list_item.get_string_owned(&[0]),
982 video_stream_id: list_item.get_int(&[1]).map(|v| v as u16),
983 });
984 }
985 Some(items)
986 } else {
987 None
988 }
989 },
990 audio_streams: {
991 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[10]) {
992 let mut items = Vec::new();
993 for list_item in l {
994 items.push(AudioStream {
995 audio_stream_name: list_item.get_string_owned(&[0]),
996 audio_stream_id: list_item.get_int(&[1]).map(|v| v as u16),
997 });
998 }
999 Some(items)
1000 } else {
1001 None
1002 }
1003 },
1004 })
1005 } else {
1006 None
1007 }
1008 } else {
1009 None
1010 }
1011 },
1012 })
1013 } else {
1014 None
1015 }
1016 } else {
1017 None
1018 }
1019 },
1020 })
1021 } else {
1022 Err(anyhow::anyhow!("Expected struct fields"))
1023 }
1024}
1025
1026pub fn decode_find_transport_response(inp: &tlv::TlvItemValue) -> anyhow::Result<FindTransportResponse> {
1028 if let tlv::TlvItemValue::List(_fields) = inp {
1029 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1030 Ok(FindTransportResponse {
1031 transport_configurations: {
1032 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
1033 let mut items = Vec::new();
1034 for list_item in l {
1035 items.push(TransportConfiguration {
1036 connection_id: list_item.get_int(&[0]).map(|v| v as u16),
1037 transport_status: list_item.get_int(&[1]).and_then(|v| TransportStatus::from_u8(v as u8)),
1038 transport_options: {
1039 if let Some(nested_tlv) = list_item.get(&[2]) {
1040 if let tlv::TlvItemValue::List(_) = nested_tlv {
1041 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
1042 Some(TransportOptions {
1043 stream_usage: nested_item.get_int(&[0]).map(|v| v as u8),
1044 video_stream_id: nested_item.get_int(&[1]).map(|v| v as u16),
1045 audio_stream_id: nested_item.get_int(&[2]).map(|v| v as u16),
1046 tls_endpoint_id: nested_item.get_int(&[3]).map(|v| v as u16),
1047 url: nested_item.get_string_owned(&[4]),
1048 trigger_options: {
1049 if let Some(nested_tlv) = nested_item.get(&[5]) {
1050 if let tlv::TlvItemValue::List(_) = nested_tlv {
1051 let nested_item = tlv::TlvItem { tag: 5, value: nested_tlv.clone() };
1052 Some(TransportTriggerOptions {
1053 trigger_type: nested_item.get_int(&[0]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
1054 motion_zones: {
1055 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[1]) {
1056 let mut items = Vec::new();
1057 for list_item in l {
1058 items.push(TransportZoneOptions {
1059 zone: list_item.get_int(&[0]).map(|v| v as u16),
1060 sensitivity: list_item.get_int(&[1]).map(|v| v as u8),
1061 });
1062 }
1063 Some(items)
1064 } else {
1065 None
1066 }
1067 },
1068 motion_sensitivity: nested_item.get_int(&[2]).map(|v| v as u8),
1069 motion_time_control: {
1070 if let Some(nested_tlv) = nested_item.get(&[3]) {
1071 if let tlv::TlvItemValue::List(_) = nested_tlv {
1072 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
1073 Some(TransportMotionTriggerTimeControl {
1074 initial_duration: nested_item.get_int(&[0]).map(|v| v as u16),
1075 augmentation_duration: nested_item.get_int(&[1]).map(|v| v as u16),
1076 max_duration: nested_item.get_int(&[2]).map(|v| v as u32),
1077 blind_duration: nested_item.get_int(&[3]).map(|v| v as u16),
1078 })
1079 } else {
1080 None
1081 }
1082 } else {
1083 None
1084 }
1085 },
1086 max_pre_roll_len: nested_item.get_int(&[4]).map(|v| v as u16),
1087 })
1088 } else {
1089 None
1090 }
1091 } else {
1092 None
1093 }
1094 },
1095 ingest_method: nested_item.get_int(&[6]).and_then(|v| IngestMethods::from_u8(v as u8)),
1096 container_options: {
1097 if let Some(nested_tlv) = nested_item.get(&[7]) {
1098 if let tlv::TlvItemValue::List(_) = nested_tlv {
1099 let nested_item = tlv::TlvItem { tag: 7, value: nested_tlv.clone() };
1100 Some(ContainerOptions {
1101 container_type: nested_item.get_int(&[0]).and_then(|v| ContainerFormat::from_u8(v as u8)),
1102 cmaf_container_options: {
1103 if let Some(nested_tlv) = nested_item.get(&[1]) {
1104 if let tlv::TlvItemValue::List(_) = nested_tlv {
1105 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
1106 Some(CMAFContainerOptions {
1107 cmaf_interface: nested_item.get_int(&[0]).and_then(|v| CMAFInterface::from_u8(v as u8)),
1108 segment_duration: nested_item.get_int(&[1]).map(|v| v as u16),
1109 chunk_duration: nested_item.get_int(&[2]).map(|v| v as u16),
1110 session_group: nested_item.get_int(&[3]).map(|v| v as u8),
1111 track_name: nested_item.get_string_owned(&[4]),
1112 cenc_key: nested_item.get_octet_string_owned(&[5]),
1113 cenc_key_id: nested_item.get_octet_string_owned(&[6]),
1114 metadata_enabled: nested_item.get_bool(&[7]),
1115 })
1116 } else {
1117 None
1118 }
1119 } else {
1120 None
1121 }
1122 },
1123 })
1124 } else {
1125 None
1126 }
1127 } else {
1128 None
1129 }
1130 },
1131 expiry_time: nested_item.get_int(&[8]),
1132 video_streams: {
1133 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[9]) {
1134 let mut items = Vec::new();
1135 for list_item in l {
1136 items.push(VideoStream {
1137 video_stream_name: list_item.get_string_owned(&[0]),
1138 video_stream_id: list_item.get_int(&[1]).map(|v| v as u16),
1139 });
1140 }
1141 Some(items)
1142 } else {
1143 None
1144 }
1145 },
1146 audio_streams: {
1147 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[10]) {
1148 let mut items = Vec::new();
1149 for list_item in l {
1150 items.push(AudioStream {
1151 audio_stream_name: list_item.get_string_owned(&[0]),
1152 audio_stream_id: list_item.get_int(&[1]).map(|v| v as u16),
1153 });
1154 }
1155 Some(items)
1156 } else {
1157 None
1158 }
1159 },
1160 })
1161 } else {
1162 None
1163 }
1164 } else {
1165 None
1166 }
1167 },
1168 });
1169 }
1170 Some(items)
1171 } else {
1172 None
1173 }
1174 },
1175 })
1176 } else {
1177 Err(anyhow::anyhow!("Expected struct fields"))
1178 }
1179}
1180
1181pub async fn allocate_push_transport(conn: &crate::controller::Connection, endpoint: u16, transport_options: TransportOptions) -> anyhow::Result<AllocatePushTransportResponse> {
1185 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_CMD_ID_ALLOCATEPUSHTRANSPORT, &encode_allocate_push_transport(transport_options)?).await?;
1186 decode_allocate_push_transport_response(&tlv)
1187}
1188
1189pub async fn deallocate_push_transport(conn: &crate::controller::Connection, endpoint: u16, connection_id: u16) -> anyhow::Result<()> {
1191 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_CMD_ID_DEALLOCATEPUSHTRANSPORT, &encode_deallocate_push_transport(connection_id)?).await?;
1192 Ok(())
1193}
1194
1195pub async fn modify_push_transport(conn: &crate::controller::Connection, endpoint: u16, connection_id: u16, transport_options: TransportOptions) -> anyhow::Result<()> {
1197 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_CMD_ID_MODIFYPUSHTRANSPORT, &encode_modify_push_transport(connection_id, transport_options)?).await?;
1198 Ok(())
1199}
1200
1201pub async fn set_transport_status(conn: &crate::controller::Connection, endpoint: u16, connection_id: Option<u16>, transport_status: TransportStatus) -> anyhow::Result<()> {
1203 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_CMD_ID_SETTRANSPORTSTATUS, &encode_set_transport_status(connection_id, transport_status)?).await?;
1204 Ok(())
1205}
1206
1207pub async fn manually_trigger_transport(conn: &crate::controller::Connection, endpoint: u16, connection_id: u16, activation_reason: TriggerActivationReason, time_control: Option<TransportMotionTriggerTimeControl>, user_defined: Option<Vec<u8>>) -> anyhow::Result<()> {
1209 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_CMD_ID_MANUALLYTRIGGERTRANSPORT, &encode_manually_trigger_transport(connection_id, activation_reason, time_control, user_defined)?).await?;
1210 Ok(())
1211}
1212
1213pub async fn find_transport(conn: &crate::controller::Connection, endpoint: u16, connection_id: Option<u16>) -> anyhow::Result<FindTransportResponse> {
1215 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_CMD_ID_FINDTRANSPORT, &encode_find_transport(connection_id)?).await?;
1216 decode_find_transport_response(&tlv)
1217}
1218
1219pub async fn read_supported_formats(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<SupportedFormat>> {
1221 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_ATTR_ID_SUPPORTEDFORMATS).await?;
1222 decode_supported_formats(&tlv)
1223}
1224
1225pub async fn read_current_connections(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TransportConfiguration>> {
1227 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_PUSH_AV_STREAM_TRANSPORT, crate::clusters::defs::CLUSTER_PUSH_AV_STREAM_TRANSPORT_ATTR_ID_CURRENTCONNECTIONS).await?;
1228 decode_current_connections(&tlv)
1229}
1230
1231#[derive(Debug, serde::Serialize)]
1232pub struct PushTransportBeginEvent {
1233 pub connection_id: Option<u16>,
1234 pub trigger_type: Option<TransportTriggerType>,
1235 pub activation_reason: Option<TriggerActivationReason>,
1236 pub container_type: Option<ContainerFormat>,
1237 pub cmaf_session_number: Option<u64>,
1238}
1239
1240#[derive(Debug, serde::Serialize)]
1241pub struct PushTransportEndEvent {
1242 pub connection_id: Option<u16>,
1243 pub container_type: Option<ContainerFormat>,
1244 pub cmaf_session_number: Option<u64>,
1245}
1246
1247pub fn decode_push_transport_begin_event(inp: &tlv::TlvItemValue) -> anyhow::Result<PushTransportBeginEvent> {
1251 if let tlv::TlvItemValue::List(_fields) = inp {
1252 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1253 Ok(PushTransportBeginEvent {
1254 connection_id: item.get_int(&[0]).map(|v| v as u16),
1255 trigger_type: item.get_int(&[1]).and_then(|v| TransportTriggerType::from_u8(v as u8)),
1256 activation_reason: item.get_int(&[2]).and_then(|v| TriggerActivationReason::from_u8(v as u8)),
1257 container_type: item.get_int(&[3]).and_then(|v| ContainerFormat::from_u8(v as u8)),
1258 cmaf_session_number: item.get_int(&[4]),
1259 })
1260 } else {
1261 Err(anyhow::anyhow!("Expected struct fields"))
1262 }
1263}
1264
1265pub fn decode_push_transport_end_event(inp: &tlv::TlvItemValue) -> anyhow::Result<PushTransportEndEvent> {
1267 if let tlv::TlvItemValue::List(_fields) = inp {
1268 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1269 Ok(PushTransportEndEvent {
1270 connection_id: item.get_int(&[0]).map(|v| v as u16),
1271 container_type: item.get_int(&[1]).and_then(|v| ContainerFormat::from_u8(v as u8)),
1272 cmaf_session_number: item.get_int(&[2]),
1273 })
1274 } else {
1275 Err(anyhow::anyhow!("Expected struct fields"))
1276 }
1277}
1278
1279
1280pub fn decode_event_json(cluster_id: u32, event_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1284 if cluster_id != 0x0555 {
1285 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0555, got {}\"}}", cluster_id);
1286 }
1287
1288 match event_id {
1289 0x00 => {
1290 match decode_push_transport_begin_event(tlv_value) {
1291 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1292 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1293 }
1294 }
1295 0x01 => {
1296 match decode_push_transport_end_event(tlv_value) {
1297 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1298 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1299 }
1300 }
1301 _ => format!("{{\"error\": \"Unknown event ID: {}\"}}", event_id),
1302 }
1303}
1304
1305pub fn get_event_list() -> Vec<(u32, &'static str)> {
1310 vec![
1311 (0x00, "PushTransportBegin"),
1312 (0x01, "PushTransportEnd"),
1313 ]
1314}
1315