matc/clusters/codec/
application_launcher.rs

1//! Matter TLV encoders and decoders for Application Launcher Cluster
2//! Cluster ID: 0x050C
3//!
4//! This file is automatically generated from ApplicationLauncher.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// Enum definitions
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum Status {
19    /// Command succeeded
20    Success = 0,
21    /// Requested app is not available
22    Appnotavailable = 1,
23    /// Video platform unable to honor command
24    Systembusy = 2,
25    /// User approval for app download is pending
26    Pendinguserapproval = 3,
27    /// Downloading the requested app
28    Downloading = 4,
29    /// Installing the requested app
30    Installing = 5,
31}
32
33impl Status {
34    /// Convert from u8 value
35    pub fn from_u8(value: u8) -> Option<Self> {
36        match value {
37            0 => Some(Status::Success),
38            1 => Some(Status::Appnotavailable),
39            2 => Some(Status::Systembusy),
40            3 => Some(Status::Pendinguserapproval),
41            4 => Some(Status::Downloading),
42            5 => Some(Status::Installing),
43            _ => None,
44        }
45    }
46
47    /// Convert to u8 value
48    pub fn to_u8(self) -> u8 {
49        self as u8
50    }
51}
52
53impl From<Status> for u8 {
54    fn from(val: Status) -> Self {
55        val as u8
56    }
57}
58
59// Struct definitions
60
61#[derive(Debug, serde::Serialize)]
62pub struct ApplicationEP {
63    pub application: Option<Application>,
64    pub endpoint: Option<u16>,
65}
66
67#[derive(Debug, serde::Serialize)]
68pub struct Application {
69    pub catalog_vendor_id: Option<u16>,
70    pub application_id: Option<String>,
71}
72
73// Command encoders
74
75/// Encode LaunchApp command (0x00)
76pub fn encode_launch_app(application: Application, data: Vec<u8>) -> anyhow::Result<Vec<u8>> {
77            // Encode struct ApplicationStruct
78            let mut application_fields = Vec::new();
79            if let Some(x) = application.catalog_vendor_id { application_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
80            if let Some(x) = application.application_id { application_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
81    let tlv = tlv::TlvItemEnc {
82        tag: 0,
83        value: tlv::TlvItemValueEnc::StructInvisible(vec![
84        (0, tlv::TlvItemValueEnc::StructInvisible(application_fields)).into(),
85        (1, tlv::TlvItemValueEnc::OctetString(data)).into(),
86        ]),
87    };
88    Ok(tlv.encode()?)
89}
90
91/// Encode StopApp command (0x01)
92pub fn encode_stop_app(application: Application) -> anyhow::Result<Vec<u8>> {
93            // Encode struct ApplicationStruct
94            let mut application_fields = Vec::new();
95            if let Some(x) = application.catalog_vendor_id { application_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
96            if let Some(x) = application.application_id { application_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
97    let tlv = tlv::TlvItemEnc {
98        tag: 0,
99        value: tlv::TlvItemValueEnc::StructInvisible(vec![
100        (0, tlv::TlvItemValueEnc::StructInvisible(application_fields)).into(),
101        ]),
102    };
103    Ok(tlv.encode()?)
104}
105
106/// Encode HideApp command (0x02)
107pub fn encode_hide_app(application: Application) -> anyhow::Result<Vec<u8>> {
108            // Encode struct ApplicationStruct
109            let mut application_fields = Vec::new();
110            if let Some(x) = application.catalog_vendor_id { application_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
111            if let Some(x) = application.application_id { application_fields.push((1, tlv::TlvItemValueEnc::String(x.clone())).into()); }
112    let tlv = tlv::TlvItemEnc {
113        tag: 0,
114        value: tlv::TlvItemValueEnc::StructInvisible(vec![
115        (0, tlv::TlvItemValueEnc::StructInvisible(application_fields)).into(),
116        ]),
117    };
118    Ok(tlv.encode()?)
119}
120
121// Attribute decoders
122
123/// Decode CatalogList attribute (0x0000)
124pub fn decode_catalog_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u16>> {
125    let mut res = Vec::new();
126    if let tlv::TlvItemValue::List(v) = inp {
127        for item in v {
128            if let tlv::TlvItemValue::Int(i) = &item.value {
129                res.push(*i as u16);
130            }
131        }
132    }
133    Ok(res)
134}
135
136/// Decode CurrentApp attribute (0x0001)
137pub fn decode_current_app(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ApplicationEP>> {
138    if let tlv::TlvItemValue::List(_fields) = inp {
139        // Struct with fields
140        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
141        Ok(Some(ApplicationEP {
142                application: {
143                    if let Some(nested_tlv) = item.get(&[0]) {
144                        if let tlv::TlvItemValue::List(_) = nested_tlv {
145                            let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
146                            Some(Application {
147                catalog_vendor_id: nested_item.get_int(&[0]).map(|v| v as u16),
148                application_id: nested_item.get_string_owned(&[1]),
149                            })
150                        } else {
151                            None
152                        }
153                    } else {
154                        None
155                    }
156                },
157                endpoint: item.get_int(&[1]).map(|v| v as u16),
158        }))
159    //} else if let tlv::TlvItemValue::Null = inp {
160    //    // Null value for nullable struct
161    //    Ok(None)
162    } else {
163    Ok(None)
164    //    Err(anyhow::anyhow!("Expected struct fields or null"))
165    }
166}
167
168
169// JSON dispatcher function
170
171/// Decode attribute value and return as JSON string
172///
173/// # Parameters
174/// * `cluster_id` - The cluster identifier
175/// * `attribute_id` - The attribute identifier
176/// * `tlv_value` - The TLV value to decode
177///
178/// # Returns
179/// JSON string representation of the decoded value or error
180pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
181    // Verify this is the correct cluster
182    if cluster_id != 0x050C {
183        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x050C, got {}\"}}", cluster_id);
184    }
185
186    match attribute_id {
187        0x0000 => {
188            match decode_catalog_list(tlv_value) {
189                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
190                Err(e) => format!("{{\"error\": \"{}\"}}", e),
191            }
192        }
193        0x0001 => {
194            match decode_current_app(tlv_value) {
195                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
196                Err(e) => format!("{{\"error\": \"{}\"}}", e),
197            }
198        }
199        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
200    }
201}
202
203/// Get list of all attributes supported by this cluster
204///
205/// # Returns
206/// Vector of tuples containing (attribute_id, attribute_name)
207pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
208    vec![
209        (0x0000, "CatalogList"),
210        (0x0001, "CurrentApp"),
211    ]
212}
213
214#[derive(Debug, serde::Serialize)]
215pub struct LauncherResponse {
216    pub status: Option<Status>,
217    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
218    pub data: Option<Vec<u8>>,
219}
220
221// Command response decoders
222
223/// Decode LauncherResponse command response (03)
224pub fn decode_launcher_response(inp: &tlv::TlvItemValue) -> anyhow::Result<LauncherResponse> {
225    if let tlv::TlvItemValue::List(_fields) = inp {
226        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
227        Ok(LauncherResponse {
228                status: item.get_int(&[0]).and_then(|v| Status::from_u8(v as u8)),
229                data: item.get_octet_string_owned(&[1]),
230        })
231    } else {
232        Err(anyhow::anyhow!("Expected struct fields"))
233    }
234}
235