matc/clusters/codec/
web_rtc_provider.rs

1//! Matter TLV encoders and decoders for WebRTC Transport Provider Cluster
2//! Cluster ID: 0x0553
3//!
4//! This file is automatically generated from WebRTC_Provider.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Import serialization helpers for octet strings
12use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14// Struct definitions
15
16#[derive(Debug, serde::Serialize)]
17pub struct SFrame {
18    pub cipher_suite: Option<u16>,
19    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
20    pub base_key: Option<Vec<u8>>,
21    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
22    pub kid: Option<Vec<u8>>,
23}
24
25// Command encoders
26
27/// Parameters for SolicitOffer command
28pub struct SolicitOfferParams {
29    pub stream_usage: u8,
30    pub originating_endpoint_id: u16,
31    pub video_stream_id: Option<u8>,
32    pub audio_stream_id: Option<u8>,
33    pub ice_transport_policy: String,
34    pub metadata_enabled: bool,
35    pub s_frame_config: SFrame,
36    pub video_streams: Vec<u8>,
37    pub audio_streams: Vec<u8>,
38}
39
40/// Encode SolicitOffer command (0x00)
41pub fn encode_solicit_offer(params: SolicitOfferParams) -> anyhow::Result<Vec<u8>> {
42            // Encode struct SFrameStruct
43            let mut s_frame_config_fields = Vec::new();
44            if let Some(x) = params.s_frame_config.cipher_suite { s_frame_config_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
45            if let Some(x) = params.s_frame_config.base_key { s_frame_config_fields.push((1, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
46            if let Some(x) = params.s_frame_config.kid { s_frame_config_fields.push((2, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
47    let tlv = tlv::TlvItemEnc {
48        tag: 0,
49        value: tlv::TlvItemValueEnc::StructInvisible(vec![
50        (0, tlv::TlvItemValueEnc::UInt8(params.stream_usage)).into(),
51        (1, tlv::TlvItemValueEnc::UInt16(params.originating_endpoint_id)).into(),
52        (2, tlv::TlvItemValueEnc::UInt8(params.video_stream_id.unwrap_or(0))).into(),
53        (3, tlv::TlvItemValueEnc::UInt8(params.audio_stream_id.unwrap_or(0))).into(),
54        (5, tlv::TlvItemValueEnc::String(params.ice_transport_policy)).into(),
55        (6, tlv::TlvItemValueEnc::Bool(params.metadata_enabled)).into(),
56        (7, tlv::TlvItemValueEnc::StructInvisible(s_frame_config_fields)).into(),
57        (8, tlv::TlvItemValueEnc::StructAnon(params.video_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
58        (9, tlv::TlvItemValueEnc::StructAnon(params.audio_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
59        ]),
60    };
61    Ok(tlv.encode()?)
62}
63
64/// Parameters for ProvideOffer command
65pub struct ProvideOfferParams {
66    pub web_rtc_session_id: Option<u8>,
67    pub sdp: String,
68    pub stream_usage: u8,
69    pub originating_endpoint_id: u16,
70    pub video_stream_id: Option<u8>,
71    pub audio_stream_id: Option<u8>,
72    pub ice_transport_policy: String,
73    pub metadata_enabled: bool,
74    pub s_frame_config: SFrame,
75    pub video_streams: Vec<u8>,
76    pub audio_streams: Vec<u8>,
77}
78
79/// Encode ProvideOffer command (0x02)
80pub fn encode_provide_offer(params: ProvideOfferParams) -> anyhow::Result<Vec<u8>> {
81            // Encode struct SFrameStruct
82            let mut s_frame_config_fields = Vec::new();
83            if let Some(x) = params.s_frame_config.cipher_suite { s_frame_config_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
84            if let Some(x) = params.s_frame_config.base_key { s_frame_config_fields.push((1, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
85            if let Some(x) = params.s_frame_config.kid { s_frame_config_fields.push((2, tlv::TlvItemValueEnc::OctetString(x.clone())).into()); }
86    let tlv = tlv::TlvItemEnc {
87        tag: 0,
88        value: tlv::TlvItemValueEnc::StructInvisible(vec![
89        (0, tlv::TlvItemValueEnc::UInt8(params.web_rtc_session_id.unwrap_or(0))).into(),
90        (1, tlv::TlvItemValueEnc::String(params.sdp)).into(),
91        (2, tlv::TlvItemValueEnc::UInt8(params.stream_usage)).into(),
92        (3, tlv::TlvItemValueEnc::UInt16(params.originating_endpoint_id)).into(),
93        (4, tlv::TlvItemValueEnc::UInt8(params.video_stream_id.unwrap_or(0))).into(),
94        (5, tlv::TlvItemValueEnc::UInt8(params.audio_stream_id.unwrap_or(0))).into(),
95        (7, tlv::TlvItemValueEnc::String(params.ice_transport_policy)).into(),
96        (8, tlv::TlvItemValueEnc::Bool(params.metadata_enabled)).into(),
97        (9, tlv::TlvItemValueEnc::StructInvisible(s_frame_config_fields)).into(),
98        (10, tlv::TlvItemValueEnc::StructAnon(params.video_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
99        (11, tlv::TlvItemValueEnc::StructAnon(params.audio_streams.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
100        ]),
101    };
102    Ok(tlv.encode()?)
103}
104
105/// Encode ProvideAnswer command (0x04)
106pub fn encode_provide_answer(web_rtc_session_id: u8, sdp: String) -> anyhow::Result<Vec<u8>> {
107    let tlv = tlv::TlvItemEnc {
108        tag: 0,
109        value: tlv::TlvItemValueEnc::StructInvisible(vec![
110        (0, tlv::TlvItemValueEnc::UInt8(web_rtc_session_id)).into(),
111        (1, tlv::TlvItemValueEnc::String(sdp)).into(),
112        ]),
113    };
114    Ok(tlv.encode()?)
115}
116
117/// Encode ProvideICECandidates command (0x05)
118pub fn encode_provide_ice_candidates(web_rtc_session_id: u8) -> anyhow::Result<Vec<u8>> {
119    let tlv = tlv::TlvItemEnc {
120        tag: 0,
121        value: tlv::TlvItemValueEnc::StructInvisible(vec![
122        (0, tlv::TlvItemValueEnc::UInt8(web_rtc_session_id)).into(),
123        ]),
124    };
125    Ok(tlv.encode()?)
126}
127
128/// Encode EndSession command (0x06)
129pub fn encode_end_session(web_rtc_session_id: u8, reason: u8) -> anyhow::Result<Vec<u8>> {
130    let tlv = tlv::TlvItemEnc {
131        tag: 0,
132        value: tlv::TlvItemValueEnc::StructInvisible(vec![
133        (0, tlv::TlvItemValueEnc::UInt8(web_rtc_session_id)).into(),
134        (1, tlv::TlvItemValueEnc::UInt8(reason)).into(),
135        ]),
136    };
137    Ok(tlv.encode()?)
138}
139
140// Attribute decoders
141
142/// Decode CurrentSessions attribute (0x0000)
143pub fn decode_current_sessions(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
144    let mut res = Vec::new();
145    if let tlv::TlvItemValue::List(v) = inp {
146        for item in v {
147            if let tlv::TlvItemValue::Int(i) = &item.value {
148                res.push(*i as u8);
149            }
150        }
151    }
152    Ok(res)
153}
154
155
156// JSON dispatcher function
157
158/// Decode attribute value and return as JSON string
159///
160/// # Parameters
161/// * `cluster_id` - The cluster identifier
162/// * `attribute_id` - The attribute identifier
163/// * `tlv_value` - The TLV value to decode
164///
165/// # Returns
166/// JSON string representation of the decoded value or error
167pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
168    // Verify this is the correct cluster
169    if cluster_id != 0x0553 {
170        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0553, got {}\"}}", cluster_id);
171    }
172
173    match attribute_id {
174        0x0000 => {
175            match decode_current_sessions(tlv_value) {
176                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
177                Err(e) => format!("{{\"error\": \"{}\"}}", e),
178            }
179        }
180        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
181    }
182}
183
184/// Get list of all attributes supported by this cluster
185///
186/// # Returns
187/// Vector of tuples containing (attribute_id, attribute_name)
188pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
189    vec![
190        (0x0000, "CurrentSessions"),
191    ]
192}
193
194#[derive(Debug, serde::Serialize)]
195pub struct SolicitOfferResponse {
196    pub web_rtc_session_id: Option<u8>,
197    pub deferred_offer: Option<bool>,
198    pub video_stream_id: Option<u8>,
199    pub audio_stream_id: Option<u8>,
200}
201
202#[derive(Debug, serde::Serialize)]
203pub struct ProvideOfferResponse {
204    pub web_rtc_session_id: Option<u8>,
205    pub video_stream_id: Option<u8>,
206    pub audio_stream_id: Option<u8>,
207}
208
209// Command response decoders
210
211/// Decode SolicitOfferResponse command response (01)
212pub fn decode_solicit_offer_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SolicitOfferResponse> {
213    if let tlv::TlvItemValue::List(_fields) = inp {
214        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
215        Ok(SolicitOfferResponse {
216                web_rtc_session_id: item.get_int(&[0]).map(|v| v as u8),
217                deferred_offer: item.get_bool(&[1]),
218                video_stream_id: item.get_int(&[2]).map(|v| v as u8),
219                audio_stream_id: item.get_int(&[3]).map(|v| v as u8),
220        })
221    } else {
222        Err(anyhow::anyhow!("Expected struct fields"))
223    }
224}
225
226/// Decode ProvideOfferResponse command response (03)
227pub fn decode_provide_offer_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ProvideOfferResponse> {
228    if let tlv::TlvItemValue::List(_fields) = inp {
229        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
230        Ok(ProvideOfferResponse {
231                web_rtc_session_id: item.get_int(&[0]).map(|v| v as u8),
232                video_stream_id: item.get_int(&[1]).map(|v| v as u8),
233                audio_stream_id: item.get_int(&[2]).map(|v| v as u8),
234        })
235    } else {
236        Err(anyhow::anyhow!("Expected struct fields"))
237    }
238}
239