1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct AreaInfo {
15 pub location_info: Option<LocationDescriptor>,
16 pub landmark_info: Option<LandmarkInfo>,
17}
18
19#[derive(Debug, serde::Serialize)]
20pub struct Area {
21 pub area_id: Option<u32>,
22 pub map_id: Option<u32>,
23 pub area_info: Option<AreaInfo>,
24}
25
26#[derive(Debug, serde::Serialize)]
27pub struct LandmarkInfo {
28 pub landmark_tag: Option<u8>,
29 pub relative_position_tag: Option<u8>,
30}
31
32#[derive(Debug, serde::Serialize)]
33pub struct Map {
34 pub map_id: Option<u32>,
35 pub name: Option<String>,
36}
37
38#[derive(Debug, serde::Serialize)]
39pub struct Progress {
40 pub area_id: Option<u32>,
41 pub status: Option<u8>,
42 pub total_operational_time: Option<u8>,
43 pub estimated_time: Option<u8>,
44}
45
46#[derive(Debug, serde::Serialize)]
47pub struct LocationDescriptor {
48 pub location_name: Option<String>,
49 pub floor_number: Option<u16>,
50 pub area_type: Option<u8>,
51}
52
53pub fn encode_select_areas(new_areas: Vec<u8>) -> anyhow::Result<Vec<u8>> {
57 let tlv = tlv::TlvItemEnc {
58 tag: 0,
59 value: tlv::TlvItemValueEnc::StructInvisible(vec![
60 (0, tlv::TlvItemValueEnc::StructAnon(new_areas.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
61 ]),
62 };
63 Ok(tlv.encode()?)
64}
65
66pub fn encode_skip_area(skipped_area: u32) -> anyhow::Result<Vec<u8>> {
68 let tlv = tlv::TlvItemEnc {
69 tag: 0,
70 value: tlv::TlvItemValueEnc::StructInvisible(vec![
71 (0, tlv::TlvItemValueEnc::UInt32(skipped_area)).into(),
72 ]),
73 };
74 Ok(tlv.encode()?)
75}
76
77pub fn decode_supported_areas(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Area>> {
81 let mut res = Vec::new();
82 if let tlv::TlvItemValue::List(v) = inp {
83 for item in v {
84 res.push(Area {
85 area_id: item.get_int(&[0]).map(|v| v as u32),
86 map_id: item.get_int(&[1]).map(|v| v as u32),
87 area_info: {
88 if let Some(nested_tlv) = item.get(&[2]) {
89 if let tlv::TlvItemValue::List(_) = nested_tlv {
90 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
91 Some(AreaInfo {
92 location_info: {
93 if let Some(nested_tlv) = nested_item.get(&[0]) {
94 if let tlv::TlvItemValue::List(_) = nested_tlv {
95 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
96 Some(LocationDescriptor {
97 location_name: nested_item.get_string_owned(&[0]),
98 floor_number: nested_item.get_int(&[1]).map(|v| v as u16),
99 area_type: nested_item.get_int(&[2]).map(|v| v as u8),
100 })
101 } else {
102 None
103 }
104 } else {
105 None
106 }
107 },
108 landmark_info: {
109 if let Some(nested_tlv) = nested_item.get(&[1]) {
110 if let tlv::TlvItemValue::List(_) = nested_tlv {
111 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
112 Some(LandmarkInfo {
113 landmark_tag: nested_item.get_int(&[0]).map(|v| v as u8),
114 relative_position_tag: nested_item.get_int(&[1]).map(|v| v as u8),
115 })
116 } else {
117 None
118 }
119 } else {
120 None
121 }
122 },
123 })
124 } else {
125 None
126 }
127 } else {
128 None
129 }
130 },
131 });
132 }
133 }
134 Ok(res)
135}
136
137pub fn decode_supported_maps(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Map>> {
139 let mut res = Vec::new();
140 if let tlv::TlvItemValue::List(v) = inp {
141 for item in v {
142 res.push(Map {
143 map_id: item.get_int(&[0]).map(|v| v as u32),
144 name: item.get_string_owned(&[1]),
145 });
146 }
147 }
148 Ok(res)
149}
150
151pub fn decode_selected_areas(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u32>> {
153 let mut res = Vec::new();
154 if let tlv::TlvItemValue::List(v) = inp {
155 for item in v {
156 if let tlv::TlvItemValue::Int(i) = &item.value {
157 res.push(*i as u32);
158 }
159 }
160 }
161 Ok(res)
162}
163
164pub fn decode_current_area(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
166 if let tlv::TlvItemValue::Int(v) = inp {
167 Ok(Some(*v as u32))
168 } else {
169 Ok(None)
170 }
171}
172
173pub fn decode_estimated_end_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
175 if let tlv::TlvItemValue::Int(v) = inp {
176 Ok(Some(*v))
177 } else {
178 Ok(None)
179 }
180}
181
182pub fn decode_progress(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Progress>> {
184 let mut res = Vec::new();
185 if let tlv::TlvItemValue::List(v) = inp {
186 for item in v {
187 res.push(Progress {
188 area_id: item.get_int(&[0]).map(|v| v as u32),
189 status: item.get_int(&[1]).map(|v| v as u8),
190 total_operational_time: item.get_int(&[2]).map(|v| v as u8),
191 estimated_time: item.get_int(&[3]).map(|v| v as u8),
192 });
193 }
194 }
195 Ok(res)
196}
197
198
199pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
211 if cluster_id != 0x0150 {
213 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0150, got {}\"}}", cluster_id);
214 }
215
216 match attribute_id {
217 0x0000 => {
218 match decode_supported_areas(tlv_value) {
219 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
220 Err(e) => format!("{{\"error\": \"{}\"}}", e),
221 }
222 }
223 0x0001 => {
224 match decode_supported_maps(tlv_value) {
225 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
226 Err(e) => format!("{{\"error\": \"{}\"}}", e),
227 }
228 }
229 0x0002 => {
230 match decode_selected_areas(tlv_value) {
231 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
232 Err(e) => format!("{{\"error\": \"{}\"}}", e),
233 }
234 }
235 0x0003 => {
236 match decode_current_area(tlv_value) {
237 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
238 Err(e) => format!("{{\"error\": \"{}\"}}", e),
239 }
240 }
241 0x0004 => {
242 match decode_estimated_end_time(tlv_value) {
243 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
244 Err(e) => format!("{{\"error\": \"{}\"}}", e),
245 }
246 }
247 0x0005 => {
248 match decode_progress(tlv_value) {
249 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
250 Err(e) => format!("{{\"error\": \"{}\"}}", e),
251 }
252 }
253 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
254 }
255}
256
257pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
262 vec![
263 (0x0000, "SupportedAreas"),
264 (0x0001, "SupportedMaps"),
265 (0x0002, "SelectedAreas"),
266 (0x0003, "CurrentArea"),
267 (0x0004, "EstimatedEndTime"),
268 (0x0005, "Progress"),
269 ]
270}
271