1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum OperationalStatus {
16 Pending = 0,
18 Operating = 1,
20 Skipped = 2,
22 Completed = 3,
24}
25
26impl OperationalStatus {
27 pub fn from_u8(value: u8) -> Option<Self> {
29 match value {
30 0 => Some(OperationalStatus::Pending),
31 1 => Some(OperationalStatus::Operating),
32 2 => Some(OperationalStatus::Skipped),
33 3 => Some(OperationalStatus::Completed),
34 _ => None,
35 }
36 }
37
38 pub fn to_u8(self) -> u8 {
40 self as u8
41 }
42}
43
44impl From<OperationalStatus> for u8 {
45 fn from(val: OperationalStatus) -> Self {
46 val as u8
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
51#[repr(u8)]
52pub enum SelectAreasStatus {
53 Success = 0,
55 Unsupportedarea = 1,
57 Invalidinmode = 2,
59 Invalidset = 3,
61}
62
63impl SelectAreasStatus {
64 pub fn from_u8(value: u8) -> Option<Self> {
66 match value {
67 0 => Some(SelectAreasStatus::Success),
68 1 => Some(SelectAreasStatus::Unsupportedarea),
69 2 => Some(SelectAreasStatus::Invalidinmode),
70 3 => Some(SelectAreasStatus::Invalidset),
71 _ => None,
72 }
73 }
74
75 pub fn to_u8(self) -> u8 {
77 self as u8
78 }
79}
80
81impl From<SelectAreasStatus> for u8 {
82 fn from(val: SelectAreasStatus) -> Self {
83 val as u8
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
88#[repr(u8)]
89pub enum SkipAreaStatus {
90 Success = 0,
92 Invalidarealist = 1,
94 Invalidinmode = 2,
96 Invalidskippedarea = 3,
98}
99
100impl SkipAreaStatus {
101 pub fn from_u8(value: u8) -> Option<Self> {
103 match value {
104 0 => Some(SkipAreaStatus::Success),
105 1 => Some(SkipAreaStatus::Invalidarealist),
106 2 => Some(SkipAreaStatus::Invalidinmode),
107 3 => Some(SkipAreaStatus::Invalidskippedarea),
108 _ => None,
109 }
110 }
111
112 pub fn to_u8(self) -> u8 {
114 self as u8
115 }
116}
117
118impl From<SkipAreaStatus> for u8 {
119 fn from(val: SkipAreaStatus) -> Self {
120 val as u8
121 }
122}
123
124#[derive(Debug, serde::Serialize)]
127pub struct AreaInfo {
128 pub location_info: Option<LocationDescriptor>,
129 pub landmark_info: Option<LandmarkInfo>,
130}
131
132#[derive(Debug, serde::Serialize)]
133pub struct Area {
134 pub area_id: Option<u32>,
135 pub map_id: Option<u32>,
136 pub area_info: Option<AreaInfo>,
137}
138
139#[derive(Debug, serde::Serialize)]
140pub struct LandmarkInfo {
141 pub landmark_tag: Option<u8>,
142 pub relative_position_tag: Option<u8>,
143}
144
145#[derive(Debug, serde::Serialize)]
146pub struct Map {
147 pub map_id: Option<u32>,
148 pub name: Option<String>,
149}
150
151#[derive(Debug, serde::Serialize)]
152pub struct Progress {
153 pub area_id: Option<u32>,
154 pub status: Option<OperationalStatus>,
155 pub total_operational_time: Option<u32>,
156 pub estimated_time: Option<u32>,
157}
158
159#[derive(Debug, serde::Serialize)]
160pub struct LocationDescriptor {
161 pub location_name: Option<String>,
162 pub floor_number: Option<u16>,
163 pub area_type: Option<u8>,
164}
165
166pub fn encode_select_areas(new_areas: Vec<u32>) -> anyhow::Result<Vec<u8>> {
170 let tlv = tlv::TlvItemEnc {
171 tag: 0,
172 value: tlv::TlvItemValueEnc::StructInvisible(vec![
173 (0, tlv::TlvItemValueEnc::StructAnon(new_areas.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt32(v)).into()).collect())).into(),
174 ]),
175 };
176 Ok(tlv.encode()?)
177}
178
179pub fn encode_skip_area(skipped_area: u32) -> anyhow::Result<Vec<u8>> {
181 let tlv = tlv::TlvItemEnc {
182 tag: 0,
183 value: tlv::TlvItemValueEnc::StructInvisible(vec![
184 (0, tlv::TlvItemValueEnc::UInt32(skipped_area)).into(),
185 ]),
186 };
187 Ok(tlv.encode()?)
188}
189
190pub fn decode_supported_areas(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Area>> {
194 let mut res = Vec::new();
195 if let tlv::TlvItemValue::List(v) = inp {
196 for item in v {
197 res.push(Area {
198 area_id: item.get_int(&[0]).map(|v| v as u32),
199 map_id: item.get_int(&[1]).map(|v| v as u32),
200 area_info: {
201 if let Some(nested_tlv) = item.get(&[2]) {
202 if let tlv::TlvItemValue::List(_) = nested_tlv {
203 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
204 Some(AreaInfo {
205 location_info: {
206 if let Some(nested_tlv) = nested_item.get(&[0]) {
207 if let tlv::TlvItemValue::List(_) = nested_tlv {
208 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
209 Some(LocationDescriptor {
210 location_name: nested_item.get_string_owned(&[0]),
211 floor_number: nested_item.get_int(&[1]).map(|v| v as u16),
212 area_type: nested_item.get_int(&[2]).map(|v| v as u8),
213 })
214 } else {
215 None
216 }
217 } else {
218 None
219 }
220 },
221 landmark_info: {
222 if let Some(nested_tlv) = nested_item.get(&[1]) {
223 if let tlv::TlvItemValue::List(_) = nested_tlv {
224 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
225 Some(LandmarkInfo {
226 landmark_tag: nested_item.get_int(&[0]).map(|v| v as u8),
227 relative_position_tag: nested_item.get_int(&[1]).map(|v| v as u8),
228 })
229 } else {
230 None
231 }
232 } else {
233 None
234 }
235 },
236 })
237 } else {
238 None
239 }
240 } else {
241 None
242 }
243 },
244 });
245 }
246 }
247 Ok(res)
248}
249
250pub fn decode_supported_maps(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Map>> {
252 let mut res = Vec::new();
253 if let tlv::TlvItemValue::List(v) = inp {
254 for item in v {
255 res.push(Map {
256 map_id: item.get_int(&[0]).map(|v| v as u32),
257 name: item.get_string_owned(&[1]),
258 });
259 }
260 }
261 Ok(res)
262}
263
264pub fn decode_selected_areas(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u32>> {
266 let mut res = Vec::new();
267 if let tlv::TlvItemValue::List(v) = inp {
268 for item in v {
269 if let tlv::TlvItemValue::Int(i) = &item.value {
270 res.push(*i as u32);
271 }
272 }
273 }
274 Ok(res)
275}
276
277pub fn decode_current_area(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
279 if let tlv::TlvItemValue::Int(v) = inp {
280 Ok(Some(*v as u32))
281 } else {
282 Ok(None)
283 }
284}
285
286pub fn decode_estimated_end_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
288 if let tlv::TlvItemValue::Int(v) = inp {
289 Ok(Some(*v))
290 } else {
291 Ok(None)
292 }
293}
294
295pub fn decode_progress(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Progress>> {
297 let mut res = Vec::new();
298 if let tlv::TlvItemValue::List(v) = inp {
299 for item in v {
300 res.push(Progress {
301 area_id: item.get_int(&[0]).map(|v| v as u32),
302 status: item.get_int(&[1]).and_then(|v| OperationalStatus::from_u8(v as u8)),
303 total_operational_time: item.get_int(&[2]).map(|v| v as u32),
304 estimated_time: item.get_int(&[3]).map(|v| v as u32),
305 });
306 }
307 }
308 Ok(res)
309}
310
311
312pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
324 if cluster_id != 0x0150 {
326 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0150, got {}\"}}", cluster_id);
327 }
328
329 match attribute_id {
330 0x0000 => {
331 match decode_supported_areas(tlv_value) {
332 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
333 Err(e) => format!("{{\"error\": \"{}\"}}", e),
334 }
335 }
336 0x0001 => {
337 match decode_supported_maps(tlv_value) {
338 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
339 Err(e) => format!("{{\"error\": \"{}\"}}", e),
340 }
341 }
342 0x0002 => {
343 match decode_selected_areas(tlv_value) {
344 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
345 Err(e) => format!("{{\"error\": \"{}\"}}", e),
346 }
347 }
348 0x0003 => {
349 match decode_current_area(tlv_value) {
350 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
351 Err(e) => format!("{{\"error\": \"{}\"}}", e),
352 }
353 }
354 0x0004 => {
355 match decode_estimated_end_time(tlv_value) {
356 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
357 Err(e) => format!("{{\"error\": \"{}\"}}", e),
358 }
359 }
360 0x0005 => {
361 match decode_progress(tlv_value) {
362 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
363 Err(e) => format!("{{\"error\": \"{}\"}}", e),
364 }
365 }
366 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
367 }
368}
369
370pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
375 vec![
376 (0x0000, "SupportedAreas"),
377 (0x0001, "SupportedMaps"),
378 (0x0002, "SelectedAreas"),
379 (0x0003, "CurrentArea"),
380 (0x0004, "EstimatedEndTime"),
381 (0x0005, "Progress"),
382 ]
383}
384
385#[derive(Debug, serde::Serialize)]
386pub struct SelectAreasResponse {
387 pub status: Option<u8>,
388 pub status_text: Option<String>,
389}
390
391#[derive(Debug, serde::Serialize)]
392pub struct SkipAreaResponse {
393 pub status: Option<u8>,
394 pub status_text: Option<String>,
395}
396
397pub fn decode_select_areas_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SelectAreasResponse> {
401 if let tlv::TlvItemValue::List(_fields) = inp {
402 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
403 Ok(SelectAreasResponse {
404 status: item.get_int(&[0]).map(|v| v as u8),
405 status_text: item.get_string_owned(&[1]),
406 })
407 } else {
408 Err(anyhow::anyhow!("Expected struct fields"))
409 }
410}
411
412pub fn decode_skip_area_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SkipAreaResponse> {
414 if let tlv::TlvItemValue::List(_fields) = inp {
415 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
416 Ok(SkipAreaResponse {
417 status: item.get_int(&[0]).map(|v| v as u8),
418 status_text: item.get_string_owned(&[1]),
419 })
420 } else {
421 Err(anyhow::anyhow!("Expected struct fields"))
422 }
423}
424