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 encode_keep_active(stay_active_duration: u32, timeout_ms: u32) -> anyhow::Result<Vec<u8>> {
162 let tlv = tlv::TlvItemEnc {
163 tag: 0,
164 value: tlv::TlvItemValueEnc::StructInvisible(vec![
165 (0, tlv::TlvItemValueEnc::UInt32(stay_active_duration)).into(),
166 (1, tlv::TlvItemValueEnc::UInt32(timeout_ms)).into(),
167 ]),
168 };
169 Ok(tlv.encode()?)
170}
171
172pub fn decode_data_model_revision(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
176 if let tlv::TlvItemValue::Int(v) = inp {
177 Ok(*v as u16)
178 } else {
179 Err(anyhow::anyhow!("Expected UInt16"))
180 }
181}
182
183pub fn decode_vendor_name(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
185 if let tlv::TlvItemValue::String(v) = inp {
186 Ok(v.clone())
187 } else {
188 Err(anyhow::anyhow!("Expected String"))
189 }
190}
191
192pub fn decode_vendor_id(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
194 if let tlv::TlvItemValue::Int(v) = inp {
195 Ok(*v as u16)
196 } else {
197 Err(anyhow::anyhow!("Expected UInt16"))
198 }
199}
200
201pub fn decode_product_name(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
203 if let tlv::TlvItemValue::String(v) = inp {
204 Ok(v.clone())
205 } else {
206 Err(anyhow::anyhow!("Expected String"))
207 }
208}
209
210pub fn decode_product_id(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
212 if let tlv::TlvItemValue::Int(v) = inp {
213 Ok(*v as u16)
214 } else {
215 Err(anyhow::anyhow!("Expected UInt16"))
216 }
217}
218
219pub fn decode_node_label(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
221 if let tlv::TlvItemValue::String(v) = inp {
222 Ok(v.clone())
223 } else {
224 Err(anyhow::anyhow!("Expected String"))
225 }
226}
227
228pub fn decode_location(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
230 if let tlv::TlvItemValue::String(v) = inp {
231 Ok(v.clone())
232 } else {
233 Err(anyhow::anyhow!("Expected String"))
234 }
235}
236
237pub fn decode_hardware_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
239 if let tlv::TlvItemValue::Int(v) = inp {
240 Ok(*v as u16)
241 } else {
242 Err(anyhow::anyhow!("Expected UInt16"))
243 }
244}
245
246pub fn decode_hardware_version_string(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
248 if let tlv::TlvItemValue::String(v) = inp {
249 Ok(v.clone())
250 } else {
251 Err(anyhow::anyhow!("Expected String"))
252 }
253}
254
255pub fn decode_software_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
257 if let tlv::TlvItemValue::Int(v) = inp {
258 Ok(*v as u32)
259 } else {
260 Err(anyhow::anyhow!("Expected UInt32"))
261 }
262}
263
264pub fn decode_software_version_string(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
266 if let tlv::TlvItemValue::String(v) = inp {
267 Ok(v.clone())
268 } else {
269 Err(anyhow::anyhow!("Expected String"))
270 }
271}
272
273pub fn decode_manufacturing_date(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
275 if let tlv::TlvItemValue::String(v) = inp {
276 Ok(v.clone())
277 } else {
278 Err(anyhow::anyhow!("Expected String"))
279 }
280}
281
282pub fn decode_part_number(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
284 if let tlv::TlvItemValue::String(v) = inp {
285 Ok(v.clone())
286 } else {
287 Err(anyhow::anyhow!("Expected String"))
288 }
289}
290
291pub fn decode_product_url(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
293 if let tlv::TlvItemValue::String(v) = inp {
294 Ok(v.clone())
295 } else {
296 Err(anyhow::anyhow!("Expected String"))
297 }
298}
299
300pub fn decode_product_label(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
302 if let tlv::TlvItemValue::String(v) = inp {
303 Ok(v.clone())
304 } else {
305 Err(anyhow::anyhow!("Expected String"))
306 }
307}
308
309pub fn decode_serial_number(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
311 if let tlv::TlvItemValue::String(v) = inp {
312 Ok(v.clone())
313 } else {
314 Err(anyhow::anyhow!("Expected String"))
315 }
316}
317
318pub fn decode_local_config_disabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
320 if let tlv::TlvItemValue::Bool(v) = inp {
321 Ok(*v)
322 } else {
323 Err(anyhow::anyhow!("Expected Bool"))
324 }
325}
326
327pub fn decode_reachable(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
329 if let tlv::TlvItemValue::Bool(v) = inp {
330 Ok(*v)
331 } else {
332 Err(anyhow::anyhow!("Expected Bool"))
333 }
334}
335
336pub fn decode_unique_id(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
338 if let tlv::TlvItemValue::String(v) = inp {
339 Ok(v.clone())
340 } else {
341 Err(anyhow::anyhow!("Expected String"))
342 }
343}
344
345pub fn decode_capability_minima(inp: &tlv::TlvItemValue) -> anyhow::Result<CapabilityMinima> {
347 if let tlv::TlvItemValue::List(_fields) = inp {
348 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
350 Ok(CapabilityMinima {
351 case_sessions_per_fabric: item.get_int(&[0]).map(|v| v as u16),
352 subscriptions_per_fabric: item.get_int(&[1]).map(|v| v as u16),
353 })
354 } else {
355 Err(anyhow::anyhow!("Expected struct fields"))
356 }
357}
358
359pub fn decode_product_appearance(inp: &tlv::TlvItemValue) -> anyhow::Result<ProductAppearance> {
361 if let tlv::TlvItemValue::List(_fields) = inp {
362 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
364 Ok(ProductAppearance {
365 finish: item.get_int(&[0]).and_then(|v| ProductFinish::from_u8(v as u8)),
366 primary_color: item.get_int(&[1]).and_then(|v| Color::from_u8(v as u8)),
367 })
368 } else {
369 Err(anyhow::anyhow!("Expected struct fields"))
370 }
371}
372
373pub fn decode_specification_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
375 if let tlv::TlvItemValue::Int(v) = inp {
376 Ok(*v as u32)
377 } else {
378 Err(anyhow::anyhow!("Expected UInt32"))
379 }
380}
381
382pub fn decode_max_paths_per_invoke(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
384 if let tlv::TlvItemValue::Int(v) = inp {
385 Ok(*v as u16)
386 } else {
387 Err(anyhow::anyhow!("Expected UInt16"))
388 }
389}
390
391pub fn decode_configuration_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
393 if let tlv::TlvItemValue::Int(v) = inp {
394 Ok(*v as u32)
395 } else {
396 Err(anyhow::anyhow!("Expected UInt32"))
397 }
398}
399
400
401pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
413 if cluster_id != 0x0039 {
415 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0039, got {}\"}}", cluster_id);
416 }
417
418 match attribute_id {
419 0x0000 => {
420 match decode_data_model_revision(tlv_value) {
421 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
422 Err(e) => format!("{{\"error\": \"{}\"}}", e),
423 }
424 }
425 0x0001 => {
426 match decode_vendor_name(tlv_value) {
427 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
428 Err(e) => format!("{{\"error\": \"{}\"}}", e),
429 }
430 }
431 0x0002 => {
432 match decode_vendor_id(tlv_value) {
433 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
434 Err(e) => format!("{{\"error\": \"{}\"}}", e),
435 }
436 }
437 0x0003 => {
438 match decode_product_name(tlv_value) {
439 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
440 Err(e) => format!("{{\"error\": \"{}\"}}", e),
441 }
442 }
443 0x0004 => {
444 match decode_product_id(tlv_value) {
445 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
446 Err(e) => format!("{{\"error\": \"{}\"}}", e),
447 }
448 }
449 0x0005 => {
450 match decode_node_label(tlv_value) {
451 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
452 Err(e) => format!("{{\"error\": \"{}\"}}", e),
453 }
454 }
455 0x0006 => {
456 match decode_location(tlv_value) {
457 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
458 Err(e) => format!("{{\"error\": \"{}\"}}", e),
459 }
460 }
461 0x0007 => {
462 match decode_hardware_version(tlv_value) {
463 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
464 Err(e) => format!("{{\"error\": \"{}\"}}", e),
465 }
466 }
467 0x0008 => {
468 match decode_hardware_version_string(tlv_value) {
469 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
470 Err(e) => format!("{{\"error\": \"{}\"}}", e),
471 }
472 }
473 0x0009 => {
474 match decode_software_version(tlv_value) {
475 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
476 Err(e) => format!("{{\"error\": \"{}\"}}", e),
477 }
478 }
479 0x000A => {
480 match decode_software_version_string(tlv_value) {
481 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
482 Err(e) => format!("{{\"error\": \"{}\"}}", e),
483 }
484 }
485 0x000B => {
486 match decode_manufacturing_date(tlv_value) {
487 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
488 Err(e) => format!("{{\"error\": \"{}\"}}", e),
489 }
490 }
491 0x000C => {
492 match decode_part_number(tlv_value) {
493 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
494 Err(e) => format!("{{\"error\": \"{}\"}}", e),
495 }
496 }
497 0x000D => {
498 match decode_product_url(tlv_value) {
499 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
500 Err(e) => format!("{{\"error\": \"{}\"}}", e),
501 }
502 }
503 0x000E => {
504 match decode_product_label(tlv_value) {
505 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
506 Err(e) => format!("{{\"error\": \"{}\"}}", e),
507 }
508 }
509 0x000F => {
510 match decode_serial_number(tlv_value) {
511 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
512 Err(e) => format!("{{\"error\": \"{}\"}}", e),
513 }
514 }
515 0x0010 => {
516 match decode_local_config_disabled(tlv_value) {
517 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
518 Err(e) => format!("{{\"error\": \"{}\"}}", e),
519 }
520 }
521 0x0011 => {
522 match decode_reachable(tlv_value) {
523 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
524 Err(e) => format!("{{\"error\": \"{}\"}}", e),
525 }
526 }
527 0x0012 => {
528 match decode_unique_id(tlv_value) {
529 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
530 Err(e) => format!("{{\"error\": \"{}\"}}", e),
531 }
532 }
533 0x0013 => {
534 match decode_capability_minima(tlv_value) {
535 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
536 Err(e) => format!("{{\"error\": \"{}\"}}", e),
537 }
538 }
539 0x0014 => {
540 match decode_product_appearance(tlv_value) {
541 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
542 Err(e) => format!("{{\"error\": \"{}\"}}", e),
543 }
544 }
545 0x0015 => {
546 match decode_specification_version(tlv_value) {
547 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
548 Err(e) => format!("{{\"error\": \"{}\"}}", e),
549 }
550 }
551 0x0016 => {
552 match decode_max_paths_per_invoke(tlv_value) {
553 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
554 Err(e) => format!("{{\"error\": \"{}\"}}", e),
555 }
556 }
557 0x0018 => {
558 match decode_configuration_version(tlv_value) {
559 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
560 Err(e) => format!("{{\"error\": \"{}\"}}", e),
561 }
562 }
563 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
564 }
565}
566
567pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
572 vec![
573 (0x0000, "DataModelRevision"),
574 (0x0001, "VendorName"),
575 (0x0002, "VendorID"),
576 (0x0003, "ProductName"),
577 (0x0004, "ProductID"),
578 (0x0005, "NodeLabel"),
579 (0x0006, "Location"),
580 (0x0007, "HardwareVersion"),
581 (0x0008, "HardwareVersionString"),
582 (0x0009, "SoftwareVersion"),
583 (0x000A, "SoftwareVersionString"),
584 (0x000B, "ManufacturingDate"),
585 (0x000C, "PartNumber"),
586 (0x000D, "ProductURL"),
587 (0x000E, "ProductLabel"),
588 (0x000F, "SerialNumber"),
589 (0x0010, "LocalConfigDisabled"),
590 (0x0011, "Reachable"),
591 (0x0012, "UniqueID"),
592 (0x0013, "CapabilityMinima"),
593 (0x0014, "ProductAppearance"),
594 (0x0015, "SpecificationVersion"),
595 (0x0016, "MaxPathsPerInvoke"),
596 (0x0018, "ConfigurationVersion"),
597 ]
598}
599
600#[derive(Debug, serde::Serialize)]
601pub struct StartUpEvent {
602 pub software_version: Option<u32>,
603}
604
605#[derive(Debug, serde::Serialize)]
606pub struct LeaveEvent {
607 pub fabric_index: Option<u8>,
608}
609
610#[derive(Debug, serde::Serialize)]
611pub struct ReachableChangedEvent {
612 pub reachable_new_value: Option<bool>,
613}
614
615#[derive(Debug, serde::Serialize)]
616pub struct ActiveChangedEvent {
617 pub promised_active_duration: Option<u32>,
618}
619
620pub fn decode_start_up_event(inp: &tlv::TlvItemValue) -> anyhow::Result<StartUpEvent> {
624 if let tlv::TlvItemValue::List(_fields) = inp {
625 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
626 Ok(StartUpEvent {
627 software_version: item.get_int(&[0]).map(|v| v as u32),
628 })
629 } else {
630 Err(anyhow::anyhow!("Expected struct fields"))
631 }
632}
633
634pub fn decode_leave_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LeaveEvent> {
636 if let tlv::TlvItemValue::List(_fields) = inp {
637 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
638 Ok(LeaveEvent {
639 fabric_index: item.get_int(&[0]).map(|v| v as u8),
640 })
641 } else {
642 Err(anyhow::anyhow!("Expected struct fields"))
643 }
644}
645
646pub fn decode_reachable_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ReachableChangedEvent> {
648 if let tlv::TlvItemValue::List(_fields) = inp {
649 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
650 Ok(ReachableChangedEvent {
651 reachable_new_value: item.get_bool(&[0]),
652 })
653 } else {
654 Err(anyhow::anyhow!("Expected struct fields"))
655 }
656}
657
658pub fn decode_active_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ActiveChangedEvent> {
660 if let tlv::TlvItemValue::List(_fields) = inp {
661 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
662 Ok(ActiveChangedEvent {
663 promised_active_duration: item.get_int(&[0]).map(|v| v as u32),
664 })
665 } else {
666 Err(anyhow::anyhow!("Expected struct fields"))
667 }
668}
669