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