matc/clusters/codec/
channel.rs

1//! Generated Matter TLV encoders and decoders for Channel Cluster
2//! Cluster ID: 0x0504
3//! 
4//! This file is automatically generated from Channel.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Struct definitions
12
13#[derive(Debug, serde::Serialize)]
14pub struct ChannelInfo {
15    pub major_number: Option<u16>,
16    pub minor_number: Option<u16>,
17    pub name: Option<String>,
18    pub call_sign: Option<String>,
19    pub affiliate_call_sign: Option<String>,
20    pub identifier: Option<String>,
21    pub type_: Option<u8>,
22}
23
24#[derive(Debug, serde::Serialize)]
25pub struct ChannelPaging {
26    pub previous_token: Option<PageToken>,
27    pub next_token: Option<PageToken>,
28}
29
30#[derive(Debug, serde::Serialize)]
31pub struct LineupInfo {
32    pub operator_name: Option<String>,
33    pub lineup_name: Option<String>,
34    pub postal_code: Option<String>,
35    pub lineup_info_type: Option<u8>,
36}
37
38#[derive(Debug, serde::Serialize)]
39pub struct PageToken {
40    pub limit: Option<u16>,
41    pub after: Option<String>,
42    pub before: Option<String>,
43}
44
45#[derive(Debug, serde::Serialize)]
46pub struct ProgramCast {
47    pub name: Option<String>,
48    pub role: Option<String>,
49}
50
51#[derive(Debug, serde::Serialize)]
52pub struct ProgramCategory {
53    pub category: Option<String>,
54    pub sub_category: Option<String>,
55}
56
57#[derive(Debug, serde::Serialize)]
58pub struct Program {
59    pub identifier: Option<String>,
60    pub channel: Option<ChannelInfo>,
61    pub start_time: Option<u64>,
62    pub end_time: Option<u64>,
63    pub title: Option<String>,
64    pub subtitle: Option<String>,
65    pub description: Option<String>,
66    pub audio_languages: Option<Vec<String>>,
67    pub ratings: Option<Vec<String>>,
68    pub thumbnail_url: Option<String>,
69    pub poster_art_url: Option<String>,
70    pub dvbi_url: Option<String>,
71    pub release_date: Option<String>,
72    pub parental_guidance_text: Option<String>,
73    pub recording_flag: Option<u8>,
74    pub series_info: Option<SeriesInfo>,
75    pub category_list: Option<Vec<ProgramCategory>>,
76    pub cast_list: Option<Vec<ProgramCast>>,
77}
78
79#[derive(Debug, serde::Serialize)]
80pub struct SeriesInfo {
81    pub season: Option<String>,
82    pub episode: Option<String>,
83}
84
85// Command encoders
86
87/// Encode ChangeChannel command (0x00)
88pub fn encode_change_channel(match_: String) -> anyhow::Result<Vec<u8>> {
89    let tlv = tlv::TlvItemEnc {
90        tag: 0,
91        value: tlv::TlvItemValueEnc::StructInvisible(vec![
92        (0, tlv::TlvItemValueEnc::String(match_)).into(),
93        ]),
94    };
95    Ok(tlv.encode()?)
96}
97
98/// Encode ChangeChannelByNumber command (0x02)
99pub fn encode_change_channel_by_number(major_number: u16, minor_number: u16) -> anyhow::Result<Vec<u8>> {
100    let tlv = tlv::TlvItemEnc {
101        tag: 0,
102        value: tlv::TlvItemValueEnc::StructInvisible(vec![
103        (0, tlv::TlvItemValueEnc::UInt16(major_number)).into(),
104        (1, tlv::TlvItemValueEnc::UInt16(minor_number)).into(),
105        ]),
106    };
107    Ok(tlv.encode()?)
108}
109
110/// Encode SkipChannel command (0x03)
111pub fn encode_skip_channel(count: i16) -> anyhow::Result<Vec<u8>> {
112    let tlv = tlv::TlvItemEnc {
113        tag: 0,
114        value: tlv::TlvItemValueEnc::StructInvisible(vec![
115        (0, tlv::TlvItemValueEnc::Int16(count)).into(),
116        ]),
117    };
118    Ok(tlv.encode()?)
119}
120
121/// Encode GetProgramGuide command (0x04)
122pub fn encode_get_program_guide(start_time: u64, end_time: u64, channel_list: Vec<u8>, page_token: Option<u8>, recording_flag: Option<u8>, external_id_list: Vec<u8>, data: Vec<u8>) -> anyhow::Result<Vec<u8>> {
123    let tlv = tlv::TlvItemEnc {
124        tag: 0,
125        value: tlv::TlvItemValueEnc::StructInvisible(vec![
126        (0, tlv::TlvItemValueEnc::UInt64(start_time)).into(),
127        (1, tlv::TlvItemValueEnc::UInt64(end_time)).into(),
128        (2, tlv::TlvItemValueEnc::StructAnon(channel_list.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
129        (3, tlv::TlvItemValueEnc::UInt8(page_token.unwrap_or_default())).into(),
130        (5, tlv::TlvItemValueEnc::UInt8(recording_flag.unwrap_or_default())).into(),
131        (6, tlv::TlvItemValueEnc::StructAnon(external_id_list.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
132        (7, tlv::TlvItemValueEnc::OctetString(data)).into(),
133        ]),
134    };
135    Ok(tlv.encode()?)
136}
137
138/// Encode RecordProgram command (0x06)
139pub fn encode_record_program(program_identifier: String, should_record_series: bool, external_id_list: Vec<u8>, data: Vec<u8>) -> anyhow::Result<Vec<u8>> {
140    let tlv = tlv::TlvItemEnc {
141        tag: 0,
142        value: tlv::TlvItemValueEnc::StructInvisible(vec![
143        (0, tlv::TlvItemValueEnc::String(program_identifier)).into(),
144        (1, tlv::TlvItemValueEnc::Bool(should_record_series)).into(),
145        (2, tlv::TlvItemValueEnc::StructAnon(external_id_list.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
146        (3, tlv::TlvItemValueEnc::OctetString(data)).into(),
147        ]),
148    };
149    Ok(tlv.encode()?)
150}
151
152/// Encode CancelRecordProgram command (0x07)
153pub fn encode_cancel_record_program(program_identifier: String, should_record_series: bool, external_id_list: Vec<u8>, data: Vec<u8>) -> anyhow::Result<Vec<u8>> {
154    let tlv = tlv::TlvItemEnc {
155        tag: 0,
156        value: tlv::TlvItemValueEnc::StructInvisible(vec![
157        (0, tlv::TlvItemValueEnc::String(program_identifier)).into(),
158        (1, tlv::TlvItemValueEnc::Bool(should_record_series)).into(),
159        (2, tlv::TlvItemValueEnc::StructAnon(external_id_list.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
160        (3, tlv::TlvItemValueEnc::OctetString(data)).into(),
161        ]),
162    };
163    Ok(tlv.encode()?)
164}
165
166// Attribute decoders
167
168/// Decode ChannelList attribute (0x0000)
169pub fn decode_channel_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ChannelInfo>> {
170    let mut res = Vec::new();
171    if let tlv::TlvItemValue::List(v) = inp {
172        for item in v {
173            res.push(ChannelInfo {
174                major_number: item.get_int(&[0]).map(|v| v as u16),
175                minor_number: item.get_int(&[1]).map(|v| v as u16),
176                name: item.get_string_owned(&[2]),
177                call_sign: item.get_string_owned(&[3]),
178                affiliate_call_sign: item.get_string_owned(&[4]),
179                identifier: item.get_string_owned(&[5]),
180                type_: item.get_int(&[6]).map(|v| v as u8),
181            });
182        }
183    }
184    Ok(res)
185}
186
187/// Decode Lineup attribute (0x0001)
188pub fn decode_lineup(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<LineupInfo>> {
189    if let tlv::TlvItemValue::List(_fields) = inp {
190        // Struct with fields
191        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
192        Ok(Some(LineupInfo {
193                operator_name: item.get_string_owned(&[0]),
194                lineup_name: item.get_string_owned(&[1]),
195                postal_code: item.get_string_owned(&[2]),
196                lineup_info_type: item.get_int(&[3]).map(|v| v as u8),
197        }))
198    //} else if let tlv::TlvItemValue::Null = inp {
199    //    // Null value for nullable struct
200    //    Ok(None)
201    } else {
202    Ok(None)
203    //    Err(anyhow::anyhow!("Expected struct fields or null"))
204    }
205}
206
207/// Decode CurrentChannel attribute (0x0002)
208pub fn decode_current_channel(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ChannelInfo>> {
209    if let tlv::TlvItemValue::List(_fields) = inp {
210        // Struct with fields
211        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
212        Ok(Some(ChannelInfo {
213                major_number: item.get_int(&[0]).map(|v| v as u16),
214                minor_number: item.get_int(&[1]).map(|v| v as u16),
215                name: item.get_string_owned(&[2]),
216                call_sign: item.get_string_owned(&[3]),
217                affiliate_call_sign: item.get_string_owned(&[4]),
218                identifier: item.get_string_owned(&[5]),
219                type_: item.get_int(&[6]).map(|v| v as u8),
220        }))
221    //} else if let tlv::TlvItemValue::Null = inp {
222    //    // Null value for nullable struct
223    //    Ok(None)
224    } else {
225    Ok(None)
226    //    Err(anyhow::anyhow!("Expected struct fields or null"))
227    }
228}
229
230
231// JSON dispatcher function
232
233/// Decode attribute value and return as JSON string
234/// 
235/// # Parameters
236/// * `cluster_id` - The cluster identifier
237/// * `attribute_id` - The attribute identifier
238/// * `tlv_value` - The TLV value to decode
239/// 
240/// # Returns
241/// JSON string representation of the decoded value or error
242pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
243    // Verify this is the correct cluster
244    if cluster_id != 0x0504 {
245        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0504, got {}\"}}", cluster_id);
246    }
247    
248    match attribute_id {
249        0x0000 => {
250            match decode_channel_list(tlv_value) {
251                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
252                Err(e) => format!("{{\"error\": \"{}\"}}", e),
253            }
254        }
255        0x0001 => {
256            match decode_lineup(tlv_value) {
257                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
258                Err(e) => format!("{{\"error\": \"{}\"}}", e),
259            }
260        }
261        0x0002 => {
262            match decode_current_channel(tlv_value) {
263                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
264                Err(e) => format!("{{\"error\": \"{}\"}}", e),
265            }
266        }
267        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
268    }
269}
270
271/// Get list of all attributes supported by this cluster
272/// 
273/// # Returns
274/// Vector of tuples containing (attribute_id, attribute_name)
275pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
276    vec![
277        (0x0000, "ChannelList"),
278        (0x0001, "Lineup"),
279        (0x0002, "CurrentChannel"),
280    ]
281}
282