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 Color {
16 Black = 0,
18 Navy = 1,
20 Green = 2,
22 Teal = 3,
24 Maroon = 4,
26 Purple = 5,
28 Olive = 6,
30 Gray = 7,
32 Blue = 8,
34 Lime = 9,
36 Aqua = 10,
38 Red = 11,
40 Fuchsia = 12,
42 Yellow = 13,
44 White = 14,
46 Nickel = 15,
48 Chrome = 16,
50 Brass = 17,
52 Copper = 18,
54 Silver = 19,
56 Gold = 20,
58}
59
60impl Color {
61 pub fn from_u8(value: u8) -> Option<Self> {
63 match value {
64 0 => Some(Color::Black),
65 1 => Some(Color::Navy),
66 2 => Some(Color::Green),
67 3 => Some(Color::Teal),
68 4 => Some(Color::Maroon),
69 5 => Some(Color::Purple),
70 6 => Some(Color::Olive),
71 7 => Some(Color::Gray),
72 8 => Some(Color::Blue),
73 9 => Some(Color::Lime),
74 10 => Some(Color::Aqua),
75 11 => Some(Color::Red),
76 12 => Some(Color::Fuchsia),
77 13 => Some(Color::Yellow),
78 14 => Some(Color::White),
79 15 => Some(Color::Nickel),
80 16 => Some(Color::Chrome),
81 17 => Some(Color::Brass),
82 18 => Some(Color::Copper),
83 19 => Some(Color::Silver),
84 20 => Some(Color::Gold),
85 _ => None,
86 }
87 }
88
89 pub fn to_u8(self) -> u8 {
91 self as u8
92 }
93}
94
95impl From<Color> for u8 {
96 fn from(val: Color) -> Self {
97 val as u8
98 }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
102#[repr(u8)]
103pub enum ProductFinish {
104 Other = 0,
106 Matte = 1,
108 Satin = 2,
110 Polished = 3,
112 Rugged = 4,
114 Fabric = 5,
116}
117
118impl ProductFinish {
119 pub fn from_u8(value: u8) -> Option<Self> {
121 match value {
122 0 => Some(ProductFinish::Other),
123 1 => Some(ProductFinish::Matte),
124 2 => Some(ProductFinish::Satin),
125 3 => Some(ProductFinish::Polished),
126 4 => Some(ProductFinish::Rugged),
127 5 => Some(ProductFinish::Fabric),
128 _ => None,
129 }
130 }
131
132 pub fn to_u8(self) -> u8 {
134 self as u8
135 }
136}
137
138impl From<ProductFinish> for u8 {
139 fn from(val: ProductFinish) -> Self {
140 val as u8
141 }
142}
143
144#[derive(Debug, serde::Serialize)]
147pub struct CapabilityMinima {
148 pub case_sessions_per_fabric: Option<u16>,
149 pub subscriptions_per_fabric: Option<u16>,
150}
151
152#[derive(Debug, serde::Serialize)]
153pub struct ProductAppearance {
154 pub finish: Option<ProductFinish>,
155 pub primary_color: Option<Color>,
156}
157
158pub fn decode_data_model_revision(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
162 if let tlv::TlvItemValue::Int(v) = inp {
163 Ok(*v as u16)
164 } else {
165 Err(anyhow::anyhow!("Expected UInt16"))
166 }
167}
168
169pub fn decode_vendor_name(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
171 if let tlv::TlvItemValue::String(v) = inp {
172 Ok(v.clone())
173 } else {
174 Err(anyhow::anyhow!("Expected String"))
175 }
176}
177
178pub fn decode_vendor_id(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
180 if let tlv::TlvItemValue::Int(v) = inp {
181 Ok(*v as u16)
182 } else {
183 Err(anyhow::anyhow!("Expected UInt16"))
184 }
185}
186
187pub fn decode_product_name(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
189 if let tlv::TlvItemValue::String(v) = inp {
190 Ok(v.clone())
191 } else {
192 Err(anyhow::anyhow!("Expected String"))
193 }
194}
195
196pub fn decode_product_id(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
198 if let tlv::TlvItemValue::Int(v) = inp {
199 Ok(*v as u16)
200 } else {
201 Err(anyhow::anyhow!("Expected UInt16"))
202 }
203}
204
205pub fn decode_node_label(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
207 if let tlv::TlvItemValue::String(v) = inp {
208 Ok(v.clone())
209 } else {
210 Err(anyhow::anyhow!("Expected String"))
211 }
212}
213
214pub fn decode_location(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
216 if let tlv::TlvItemValue::String(v) = inp {
217 Ok(v.clone())
218 } else {
219 Err(anyhow::anyhow!("Expected String"))
220 }
221}
222
223pub fn decode_hardware_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
225 if let tlv::TlvItemValue::Int(v) = inp {
226 Ok(*v as u16)
227 } else {
228 Err(anyhow::anyhow!("Expected UInt16"))
229 }
230}
231
232pub fn decode_hardware_version_string(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
234 if let tlv::TlvItemValue::String(v) = inp {
235 Ok(v.clone())
236 } else {
237 Err(anyhow::anyhow!("Expected String"))
238 }
239}
240
241pub fn decode_software_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
243 if let tlv::TlvItemValue::Int(v) = inp {
244 Ok(*v as u32)
245 } else {
246 Err(anyhow::anyhow!("Expected UInt32"))
247 }
248}
249
250pub fn decode_software_version_string(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
252 if let tlv::TlvItemValue::String(v) = inp {
253 Ok(v.clone())
254 } else {
255 Err(anyhow::anyhow!("Expected String"))
256 }
257}
258
259pub fn decode_manufacturing_date(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
261 if let tlv::TlvItemValue::String(v) = inp {
262 Ok(v.clone())
263 } else {
264 Err(anyhow::anyhow!("Expected String"))
265 }
266}
267
268pub fn decode_part_number(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
270 if let tlv::TlvItemValue::String(v) = inp {
271 Ok(v.clone())
272 } else {
273 Err(anyhow::anyhow!("Expected String"))
274 }
275}
276
277pub fn decode_product_url(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
279 if let tlv::TlvItemValue::String(v) = inp {
280 Ok(v.clone())
281 } else {
282 Err(anyhow::anyhow!("Expected String"))
283 }
284}
285
286pub fn decode_product_label(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
288 if let tlv::TlvItemValue::String(v) = inp {
289 Ok(v.clone())
290 } else {
291 Err(anyhow::anyhow!("Expected String"))
292 }
293}
294
295pub fn decode_serial_number(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
297 if let tlv::TlvItemValue::String(v) = inp {
298 Ok(v.clone())
299 } else {
300 Err(anyhow::anyhow!("Expected String"))
301 }
302}
303
304pub fn decode_local_config_disabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
306 if let tlv::TlvItemValue::Bool(v) = inp {
307 Ok(*v)
308 } else {
309 Err(anyhow::anyhow!("Expected Bool"))
310 }
311}
312
313pub fn decode_reachable(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
315 if let tlv::TlvItemValue::Bool(v) = inp {
316 Ok(*v)
317 } else {
318 Err(anyhow::anyhow!("Expected Bool"))
319 }
320}
321
322pub fn decode_unique_id(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
324 if let tlv::TlvItemValue::String(v) = inp {
325 Ok(v.clone())
326 } else {
327 Err(anyhow::anyhow!("Expected String"))
328 }
329}
330
331pub fn decode_capability_minima(inp: &tlv::TlvItemValue) -> anyhow::Result<CapabilityMinima> {
333 if let tlv::TlvItemValue::List(_fields) = inp {
334 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
336 Ok(CapabilityMinima {
337 case_sessions_per_fabric: item.get_int(&[0]).map(|v| v as u16),
338 subscriptions_per_fabric: item.get_int(&[1]).map(|v| v as u16),
339 })
340 } else {
341 Err(anyhow::anyhow!("Expected struct fields"))
342 }
343}
344
345pub fn decode_product_appearance(inp: &tlv::TlvItemValue) -> anyhow::Result<ProductAppearance> {
347 if let tlv::TlvItemValue::List(_fields) = inp {
348 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
350 Ok(ProductAppearance {
351 finish: item.get_int(&[0]).and_then(|v| ProductFinish::from_u8(v as u8)),
352 primary_color: item.get_int(&[1]).and_then(|v| Color::from_u8(v as u8)),
353 })
354 } else {
355 Err(anyhow::anyhow!("Expected struct fields"))
356 }
357}
358
359pub fn decode_specification_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
361 if let tlv::TlvItemValue::Int(v) = inp {
362 Ok(*v as u32)
363 } else {
364 Err(anyhow::anyhow!("Expected UInt32"))
365 }
366}
367
368pub fn decode_max_paths_per_invoke(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
370 if let tlv::TlvItemValue::Int(v) = inp {
371 Ok(*v as u16)
372 } else {
373 Err(anyhow::anyhow!("Expected UInt16"))
374 }
375}
376
377pub fn decode_configuration_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
379 if let tlv::TlvItemValue::Int(v) = inp {
380 Ok(*v as u32)
381 } else {
382 Err(anyhow::anyhow!("Expected UInt32"))
383 }
384}
385
386
387pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
399 if cluster_id != 0x0028 {
401 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0028, got {}\"}}", cluster_id);
402 }
403
404 match attribute_id {
405 0x0000 => {
406 match decode_data_model_revision(tlv_value) {
407 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
408 Err(e) => format!("{{\"error\": \"{}\"}}", e),
409 }
410 }
411 0x0001 => {
412 match decode_vendor_name(tlv_value) {
413 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
414 Err(e) => format!("{{\"error\": \"{}\"}}", e),
415 }
416 }
417 0x0002 => {
418 match decode_vendor_id(tlv_value) {
419 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
420 Err(e) => format!("{{\"error\": \"{}\"}}", e),
421 }
422 }
423 0x0003 => {
424 match decode_product_name(tlv_value) {
425 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
426 Err(e) => format!("{{\"error\": \"{}\"}}", e),
427 }
428 }
429 0x0004 => {
430 match decode_product_id(tlv_value) {
431 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
432 Err(e) => format!("{{\"error\": \"{}\"}}", e),
433 }
434 }
435 0x0005 => {
436 match decode_node_label(tlv_value) {
437 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
438 Err(e) => format!("{{\"error\": \"{}\"}}", e),
439 }
440 }
441 0x0006 => {
442 match decode_location(tlv_value) {
443 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
444 Err(e) => format!("{{\"error\": \"{}\"}}", e),
445 }
446 }
447 0x0007 => {
448 match decode_hardware_version(tlv_value) {
449 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
450 Err(e) => format!("{{\"error\": \"{}\"}}", e),
451 }
452 }
453 0x0008 => {
454 match decode_hardware_version_string(tlv_value) {
455 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
456 Err(e) => format!("{{\"error\": \"{}\"}}", e),
457 }
458 }
459 0x0009 => {
460 match decode_software_version(tlv_value) {
461 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
462 Err(e) => format!("{{\"error\": \"{}\"}}", e),
463 }
464 }
465 0x000A => {
466 match decode_software_version_string(tlv_value) {
467 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
468 Err(e) => format!("{{\"error\": \"{}\"}}", e),
469 }
470 }
471 0x000B => {
472 match decode_manufacturing_date(tlv_value) {
473 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
474 Err(e) => format!("{{\"error\": \"{}\"}}", e),
475 }
476 }
477 0x000C => {
478 match decode_part_number(tlv_value) {
479 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
480 Err(e) => format!("{{\"error\": \"{}\"}}", e),
481 }
482 }
483 0x000D => {
484 match decode_product_url(tlv_value) {
485 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
486 Err(e) => format!("{{\"error\": \"{}\"}}", e),
487 }
488 }
489 0x000E => {
490 match decode_product_label(tlv_value) {
491 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
492 Err(e) => format!("{{\"error\": \"{}\"}}", e),
493 }
494 }
495 0x000F => {
496 match decode_serial_number(tlv_value) {
497 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
498 Err(e) => format!("{{\"error\": \"{}\"}}", e),
499 }
500 }
501 0x0010 => {
502 match decode_local_config_disabled(tlv_value) {
503 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
504 Err(e) => format!("{{\"error\": \"{}\"}}", e),
505 }
506 }
507 0x0011 => {
508 match decode_reachable(tlv_value) {
509 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
510 Err(e) => format!("{{\"error\": \"{}\"}}", e),
511 }
512 }
513 0x0012 => {
514 match decode_unique_id(tlv_value) {
515 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
516 Err(e) => format!("{{\"error\": \"{}\"}}", e),
517 }
518 }
519 0x0013 => {
520 match decode_capability_minima(tlv_value) {
521 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
522 Err(e) => format!("{{\"error\": \"{}\"}}", e),
523 }
524 }
525 0x0014 => {
526 match decode_product_appearance(tlv_value) {
527 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
528 Err(e) => format!("{{\"error\": \"{}\"}}", e),
529 }
530 }
531 0x0015 => {
532 match decode_specification_version(tlv_value) {
533 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
534 Err(e) => format!("{{\"error\": \"{}\"}}", e),
535 }
536 }
537 0x0016 => {
538 match decode_max_paths_per_invoke(tlv_value) {
539 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
540 Err(e) => format!("{{\"error\": \"{}\"}}", e),
541 }
542 }
543 0x0018 => {
544 match decode_configuration_version(tlv_value) {
545 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
546 Err(e) => format!("{{\"error\": \"{}\"}}", e),
547 }
548 }
549 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
550 }
551}
552
553pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
558 vec![
559 (0x0000, "DataModelRevision"),
560 (0x0001, "VendorName"),
561 (0x0002, "VendorID"),
562 (0x0003, "ProductName"),
563 (0x0004, "ProductID"),
564 (0x0005, "NodeLabel"),
565 (0x0006, "Location"),
566 (0x0007, "HardwareVersion"),
567 (0x0008, "HardwareVersionString"),
568 (0x0009, "SoftwareVersion"),
569 (0x000A, "SoftwareVersionString"),
570 (0x000B, "ManufacturingDate"),
571 (0x000C, "PartNumber"),
572 (0x000D, "ProductURL"),
573 (0x000E, "ProductLabel"),
574 (0x000F, "SerialNumber"),
575 (0x0010, "LocalConfigDisabled"),
576 (0x0011, "Reachable"),
577 (0x0012, "UniqueID"),
578 (0x0013, "CapabilityMinima"),
579 (0x0014, "ProductAppearance"),
580 (0x0015, "SpecificationVersion"),
581 (0x0016, "MaxPathsPerInvoke"),
582 (0x0018, "ConfigurationVersion"),
583 ]
584}
585
586#[derive(Debug, serde::Serialize)]
587pub struct StartUpEvent {
588 pub software_version: Option<u32>,
589}
590
591#[derive(Debug, serde::Serialize)]
592pub struct LeaveEvent {
593 pub fabric_index: Option<u8>,
594}
595
596#[derive(Debug, serde::Serialize)]
597pub struct ReachableChangedEvent {
598 pub reachable_new_value: Option<bool>,
599}
600
601pub fn decode_start_up_event(inp: &tlv::TlvItemValue) -> anyhow::Result<StartUpEvent> {
605 if let tlv::TlvItemValue::List(_fields) = inp {
606 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
607 Ok(StartUpEvent {
608 software_version: item.get_int(&[0]).map(|v| v as u32),
609 })
610 } else {
611 Err(anyhow::anyhow!("Expected struct fields"))
612 }
613}
614
615pub fn decode_leave_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LeaveEvent> {
617 if let tlv::TlvItemValue::List(_fields) = inp {
618 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
619 Ok(LeaveEvent {
620 fabric_index: item.get_int(&[0]).map(|v| v as u8),
621 })
622 } else {
623 Err(anyhow::anyhow!("Expected struct fields"))
624 }
625}
626
627pub fn decode_reachable_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ReachableChangedEvent> {
629 if let tlv::TlvItemValue::List(_fields) = inp {
630 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
631 Ok(ReachableChangedEvent {
632 reachable_new_value: item.get_bool(&[0]),
633 })
634 } else {
635 Err(anyhow::anyhow!("Expected struct fields"))
636 }
637}
638