matc/clusters/codec/
application_launcher.rs1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum Status {
19 Success = 0,
21 Appnotavailable = 1,
23 Systembusy = 2,
25 Pendinguserapproval = 3,
27 Downloading = 4,
29 Installing = 5,
31}
32
33impl Status {
34 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 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#[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
73pub fn encode_launch_app(application: Application, data: Vec<u8>) -> anyhow::Result<Vec<u8>> {
77 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
91pub fn encode_stop_app(application: Application) -> anyhow::Result<Vec<u8>> {
93 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
106pub fn encode_hide_app(application: Application) -> anyhow::Result<Vec<u8>> {
108 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
121pub 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
136pub fn decode_current_app(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ApplicationEP>> {
138 if let tlv::TlvItemValue::List(_fields) = inp {
139 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 {
163 Ok(None)
164 }
166}
167
168
169pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
181 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
203pub 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
221pub 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