matc/clusters/codec/
basic_information_cluster.rs

1//! Matter TLV encoders and decoders for Basic Information Cluster
2//! Cluster ID: 0x0028
3//!
4//! This file is automatically generated from BasicInformationCluster.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Enum definitions
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum Color {
16    /// Approximately RGB #000000.
17    Black = 0,
18    /// Approximately RGB #000080.
19    Navy = 1,
20    /// Approximately RGB #008000.
21    Green = 2,
22    /// Approximately RGB #008080.
23    Teal = 3,
24    /// Approximately RGB #800000.
25    Maroon = 4,
26    /// Approximately RGB #800080.
27    Purple = 5,
28    /// Approximately RGB #808000.
29    Olive = 6,
30    /// Approximately RGB #808080.
31    Gray = 7,
32    /// Approximately RGB #0000FF.
33    Blue = 8,
34    /// Approximately RGB #00FF00.
35    Lime = 9,
36    /// Approximately RGB #00FFFF.
37    Aqua = 10,
38    /// Approximately RGB #FF0000.
39    Red = 11,
40    /// Approximately RGB #FF00FF.
41    Fuchsia = 12,
42    /// Approximately RGB #FFFF00.
43    Yellow = 13,
44    /// Approximately RGB #FFFFFF.
45    White = 14,
46    /// Typical hardware "Nickel" color.
47    Nickel = 15,
48    /// Typical hardware "Chrome" color.
49    Chrome = 16,
50    /// Typical hardware "Brass" color.
51    Brass = 17,
52    /// Typical hardware "Copper" color.
53    Copper = 18,
54    /// Typical hardware "Silver" color.
55    Silver = 19,
56    /// Typical hardware "Gold" color.
57    Gold = 20,
58}
59
60impl Color {
61    /// Convert from u8 value
62    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    /// Convert to u8 value
90    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    /// Product has some other finish not listed below.
105    Other = 0,
106    /// Product has a matte finish.
107    Matte = 1,
108    /// Product has a satin finish.
109    Satin = 2,
110    /// Product has a polished or shiny finish.
111    Polished = 3,
112    /// Product has a rugged finish.
113    Rugged = 4,
114    /// Product has a fabric finish.
115    Fabric = 5,
116}
117
118impl ProductFinish {
119    /// Convert from u8 value
120    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    /// Convert to u8 value
133    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// Struct definitions
145
146#[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
158// Attribute decoders
159
160/// Decode DataModelRevision attribute (0x0000)
161pub 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
169/// Decode VendorName attribute (0x0001)
170pub 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
178/// Decode VendorID attribute (0x0002)
179pub 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
187/// Decode ProductName attribute (0x0003)
188pub 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
196/// Decode ProductID attribute (0x0004)
197pub 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
205/// Decode NodeLabel attribute (0x0005)
206pub 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
214/// Decode Location attribute (0x0006)
215pub 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
223/// Decode HardwareVersion attribute (0x0007)
224pub 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
232/// Decode HardwareVersionString attribute (0x0008)
233pub 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
241/// Decode SoftwareVersion attribute (0x0009)
242pub 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
250/// Decode SoftwareVersionString attribute (0x000A)
251pub 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
259/// Decode ManufacturingDate attribute (0x000B)
260pub 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
268/// Decode PartNumber attribute (0x000C)
269pub 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
277/// Decode ProductURL attribute (0x000D)
278pub 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
286/// Decode ProductLabel attribute (0x000E)
287pub 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
295/// Decode SerialNumber attribute (0x000F)
296pub 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
304/// Decode LocalConfigDisabled attribute (0x0010)
305pub 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
313/// Decode Reachable attribute (0x0011)
314pub 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
322/// Decode UniqueID attribute (0x0012)
323pub 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
331/// Decode CapabilityMinima attribute (0x0013)
332pub fn decode_capability_minima(inp: &tlv::TlvItemValue) -> anyhow::Result<CapabilityMinima> {
333    if let tlv::TlvItemValue::List(_fields) = inp {
334        // Struct with fields
335        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
345/// Decode ProductAppearance attribute (0x0014)
346pub fn decode_product_appearance(inp: &tlv::TlvItemValue) -> anyhow::Result<ProductAppearance> {
347    if let tlv::TlvItemValue::List(_fields) = inp {
348        // Struct with fields
349        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
359/// Decode SpecificationVersion attribute (0x0015)
360pub 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
368/// Decode MaxPathsPerInvoke attribute (0x0016)
369pub 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
377/// Decode ConfigurationVersion attribute (0x0018)
378pub 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
387// JSON dispatcher function
388
389/// Decode attribute value and return as JSON string
390///
391/// # Parameters
392/// * `cluster_id` - The cluster identifier
393/// * `attribute_id` - The attribute identifier
394/// * `tlv_value` - The TLV value to decode
395///
396/// # Returns
397/// JSON string representation of the decoded value or error
398pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
399    // Verify this is the correct cluster
400    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
553/// Get list of all attributes supported by this cluster
554///
555/// # Returns
556/// Vector of tuples containing (attribute_id, attribute_name)
557pub 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
601// Event decoders
602
603/// Decode StartUp event (0x00, priority: critical)
604pub 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
615/// Decode Leave event (0x02, priority: info)
616pub 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
627/// Decode ReachableChanged event (0x03, priority: info)
628pub 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