1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct DSTOffset {
15 pub offset: Option<i32>,
16 pub valid_starting: Option<u8>,
17 pub valid_until: Option<u8>,
18}
19
20#[derive(Debug, serde::Serialize)]
21pub struct FabricScopedTrustedTimeSource {
22 pub node_id: Option<u64>,
23 pub endpoint: Option<u16>,
24}
25
26#[derive(Debug, serde::Serialize)]
27pub struct TimeZone {
28 pub offset: Option<i32>,
29 pub valid_at: Option<u8>,
30 pub name: Option<String>,
31}
32
33#[derive(Debug, serde::Serialize)]
34pub struct TrustedTimeSource {
35 pub fabric_index: Option<u8>,
36 pub node_id: Option<u64>,
37 pub endpoint: Option<u16>,
38}
39
40pub fn encode_set_utc_time(utc_time: u8, granularity: u8, time_source: u8) -> anyhow::Result<Vec<u8>> {
44 let tlv = tlv::TlvItemEnc {
45 tag: 0,
46 value: tlv::TlvItemValueEnc::StructInvisible(vec![
47 (0, tlv::TlvItemValueEnc::UInt8(utc_time)).into(),
48 (1, tlv::TlvItemValueEnc::UInt8(granularity)).into(),
49 (2, tlv::TlvItemValueEnc::UInt8(time_source)).into(),
50 ]),
51 };
52 Ok(tlv.encode()?)
53}
54
55pub fn encode_set_trusted_time_source(trusted_time_source: Option<u8>) -> anyhow::Result<Vec<u8>> {
57 let tlv = tlv::TlvItemEnc {
58 tag: 0,
59 value: tlv::TlvItemValueEnc::StructInvisible(vec![
60 (0, tlv::TlvItemValueEnc::UInt8(trusted_time_source.unwrap_or(0))).into(),
61 ]),
62 };
63 Ok(tlv.encode()?)
64}
65
66pub fn encode_set_time_zone(time_zone: Vec<u8>) -> anyhow::Result<Vec<u8>> {
68 let tlv = tlv::TlvItemEnc {
69 tag: 0,
70 value: tlv::TlvItemValueEnc::StructInvisible(vec![
71 (0, tlv::TlvItemValueEnc::StructAnon(time_zone.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
72 ]),
73 };
74 Ok(tlv.encode()?)
75}
76
77pub fn encode_set_dst_offset(dst_offset: Vec<u8>) -> anyhow::Result<Vec<u8>> {
79 let tlv = tlv::TlvItemEnc {
80 tag: 0,
81 value: tlv::TlvItemValueEnc::StructInvisible(vec![
82 (0, tlv::TlvItemValueEnc::StructAnon(dst_offset.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
83 ]),
84 };
85 Ok(tlv.encode()?)
86}
87
88pub fn encode_set_default_ntp(default_ntp: Option<String>) -> anyhow::Result<Vec<u8>> {
90 let tlv = tlv::TlvItemEnc {
91 tag: 0,
92 value: tlv::TlvItemValueEnc::StructInvisible(vec![
93 (0, tlv::TlvItemValueEnc::String(default_ntp.unwrap_or("".to_string()))).into(),
94 ]),
95 };
96 Ok(tlv.encode()?)
97}
98
99pub fn decode_utc_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
103 if let tlv::TlvItemValue::Int(v) = inp {
104 Ok(Some(*v as u8))
105 } else {
106 Ok(None)
107 }
108}
109
110pub fn decode_granularity(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
112 if let tlv::TlvItemValue::Int(v) = inp {
113 Ok(*v as u8)
114 } else {
115 Err(anyhow::anyhow!("Expected Integer"))
116 }
117}
118
119pub fn decode_time_source(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
121 if let tlv::TlvItemValue::Int(v) = inp {
122 Ok(*v as u8)
123 } else {
124 Err(anyhow::anyhow!("Expected Integer"))
125 }
126}
127
128pub fn decode_trusted_time_source(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<TrustedTimeSource>> {
130 if let tlv::TlvItemValue::List(_fields) = inp {
131 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
133 Ok(Some(TrustedTimeSource {
134 fabric_index: item.get_int(&[0]).map(|v| v as u8),
135 node_id: item.get_int(&[1]),
136 endpoint: item.get_int(&[2]).map(|v| v as u16),
137 }))
138 } else {
142 Ok(None)
143 }
145}
146
147pub fn decode_default_ntp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<String>> {
149 if let tlv::TlvItemValue::String(v) = inp {
150 Ok(Some(v.clone()))
151 } else {
152 Ok(None)
153 }
154}
155
156pub fn decode_time_zone(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TimeZone>> {
158 let mut res = Vec::new();
159 if let tlv::TlvItemValue::List(v) = inp {
160 for item in v {
161 res.push(TimeZone {
162 offset: item.get_int(&[0]).map(|v| v as i32),
163 valid_at: item.get_int(&[1]).map(|v| v as u8),
164 name: item.get_string_owned(&[2]),
165 });
166 }
167 }
168 Ok(res)
169}
170
171pub fn decode_dst_offset(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DSTOffset>> {
173 let mut res = Vec::new();
174 if let tlv::TlvItemValue::List(v) = inp {
175 for item in v {
176 res.push(DSTOffset {
177 offset: item.get_int(&[0]).map(|v| v as i32),
178 valid_starting: item.get_int(&[1]).map(|v| v as u8),
179 valid_until: item.get_int(&[2]).map(|v| v as u8),
180 });
181 }
182 }
183 Ok(res)
184}
185
186pub fn decode_local_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
188 if let tlv::TlvItemValue::Int(v) = inp {
189 Ok(Some(*v as u8))
190 } else {
191 Ok(None)
192 }
193}
194
195pub fn decode_time_zone_database(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
197 if let tlv::TlvItemValue::Int(v) = inp {
198 Ok(*v as u8)
199 } else {
200 Err(anyhow::anyhow!("Expected Integer"))
201 }
202}
203
204pub fn decode_ntp_server_available(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
206 if let tlv::TlvItemValue::Bool(v) = inp {
207 Ok(*v)
208 } else {
209 Err(anyhow::anyhow!("Expected Bool"))
210 }
211}
212
213pub fn decode_time_zone_list_max_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
215 if let tlv::TlvItemValue::Int(v) = inp {
216 Ok(*v as u8)
217 } else {
218 Err(anyhow::anyhow!("Expected Integer"))
219 }
220}
221
222pub fn decode_dst_offset_list_max_size(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
224 if let tlv::TlvItemValue::Int(v) = inp {
225 Ok(*v as u8)
226 } else {
227 Err(anyhow::anyhow!("Expected Integer"))
228 }
229}
230
231pub fn decode_supports_dns_resolve(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
233 if let tlv::TlvItemValue::Bool(v) = inp {
234 Ok(*v)
235 } else {
236 Err(anyhow::anyhow!("Expected Bool"))
237 }
238}
239
240
241pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
253 if cluster_id != 0x0038 {
255 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0038, got {}\"}}", cluster_id);
256 }
257
258 match attribute_id {
259 0x0000 => {
260 match decode_utc_time(tlv_value) {
261 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
262 Err(e) => format!("{{\"error\": \"{}\"}}", e),
263 }
264 }
265 0x0001 => {
266 match decode_granularity(tlv_value) {
267 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
268 Err(e) => format!("{{\"error\": \"{}\"}}", e),
269 }
270 }
271 0x0002 => {
272 match decode_time_source(tlv_value) {
273 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
274 Err(e) => format!("{{\"error\": \"{}\"}}", e),
275 }
276 }
277 0x0003 => {
278 match decode_trusted_time_source(tlv_value) {
279 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
280 Err(e) => format!("{{\"error\": \"{}\"}}", e),
281 }
282 }
283 0x0004 => {
284 match decode_default_ntp(tlv_value) {
285 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
286 Err(e) => format!("{{\"error\": \"{}\"}}", e),
287 }
288 }
289 0x0005 => {
290 match decode_time_zone(tlv_value) {
291 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
292 Err(e) => format!("{{\"error\": \"{}\"}}", e),
293 }
294 }
295 0x0006 => {
296 match decode_dst_offset(tlv_value) {
297 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
298 Err(e) => format!("{{\"error\": \"{}\"}}", e),
299 }
300 }
301 0x0007 => {
302 match decode_local_time(tlv_value) {
303 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
304 Err(e) => format!("{{\"error\": \"{}\"}}", e),
305 }
306 }
307 0x0008 => {
308 match decode_time_zone_database(tlv_value) {
309 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
310 Err(e) => format!("{{\"error\": \"{}\"}}", e),
311 }
312 }
313 0x0009 => {
314 match decode_ntp_server_available(tlv_value) {
315 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
316 Err(e) => format!("{{\"error\": \"{}\"}}", e),
317 }
318 }
319 0x000A => {
320 match decode_time_zone_list_max_size(tlv_value) {
321 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
322 Err(e) => format!("{{\"error\": \"{}\"}}", e),
323 }
324 }
325 0x000B => {
326 match decode_dst_offset_list_max_size(tlv_value) {
327 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
328 Err(e) => format!("{{\"error\": \"{}\"}}", e),
329 }
330 }
331 0x000C => {
332 match decode_supports_dns_resolve(tlv_value) {
333 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
334 Err(e) => format!("{{\"error\": \"{}\"}}", e),
335 }
336 }
337 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
338 }
339}
340
341pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
346 vec![
347 (0x0000, "UTCTime"),
348 (0x0001, "Granularity"),
349 (0x0002, "TimeSource"),
350 (0x0003, "TrustedTimeSource"),
351 (0x0004, "DefaultNTP"),
352 (0x0005, "TimeZone"),
353 (0x0006, "DSTOffset"),
354 (0x0007, "LocalTime"),
355 (0x0008, "TimeZoneDatabase"),
356 (0x0009, "NTPServerAvailable"),
357 (0x000A, "TimeZoneListMaxSize"),
358 (0x000B, "DSTOffsetListMaxSize"),
359 (0x000C, "SupportsDNSResolve"),
360 ]
361}
362