matc/device/
attributes.rs

1use anyhow::Result;
2
3use crate::{clusters, tlv};
4
5use super::Device;
6
7// Global attribute IDs (Matter spec section 7.13)
8const ATTR_GENERATED_CMD_LIST: u32 = 0xFFF8;
9const ATTR_ACCEPTED_CMD_LIST: u32 = 0xFFF9;
10const ATTR_EVENT_LIST: u32 = 0xFFFA;
11const ATTR_ATTRIBUTE_LIST: u32 = 0xFFFB;
12const ATTR_FEATURE_MAP: u32 = 0xFFFC;
13const ATTR_CLUSTER_REVISION: u32 = 0xFFFD;
14
15impl Device {
16    pub(super) fn setup_default_attributes(&mut self) -> Result<()> {
17        use clusters::defs::*;
18
19
20        let mut buf = tlv::TlvBuffer::new();
21        buf.write_array(2)?;
22        buf.write_anon_struct()?;
23        buf.write_uint32(0, 0x0016)?; // DeviceType = Root Node
24        buf.write_uint16(1, 2)?; // Revision = 2
25        buf.write_struct_end()?;
26        buf.write_struct_end()?;
27        self.set_attribute_raw(
28            0,
29            CLUSTER_ID_DESCRIPTOR,
30            CLUSTER_DESCRIPTOR_ATTR_ID_DEVICETYPELIST,
31            &buf.data,
32        );
33
34        let mut buf = tlv::TlvBuffer::new();
35        buf.write_array(2)?;
36        buf.write_uint32_notag(CLUSTER_ID_DESCRIPTOR)?;
37        buf.write_uint32_notag(CLUSTER_ID_ACCESS_CONTROL)?;
38        buf.write_uint32_notag(CLUSTER_ID_BASIC_INFORMATION)?;
39        buf.write_uint32_notag(CLUSTER_ID_GENERAL_COMMISSIONING)?;
40        buf.write_uint32_notag(CLUSTER_ID_NETWORK_COMMISSIONING)?;
41        buf.write_uint32_notag(CLUSTER_ID_ADMINISTRATOR_COMMISSIONING)?;
42        buf.write_uint32_notag(CLUSTER_ID_OPERATIONAL_CREDENTIALS)?;
43        buf.write_struct_end()?;
44        self.set_attribute_raw(
45            0,
46            CLUSTER_ID_DESCRIPTOR,
47            CLUSTER_DESCRIPTOR_ATTR_ID_SERVERLIST,
48            &buf.data,
49        );
50
51        self.set_empty_array(0, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_CLIENTLIST);
52
53        self.set_empty_array(0, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_PARTSLIST);
54
55        self.set_cluster_globals(
56            0,
57            CLUSTER_ID_DESCRIPTOR,
58            3,
59            0,
60            &[
61                CLUSTER_DESCRIPTOR_ATTR_ID_DEVICETYPELIST,
62                CLUSTER_DESCRIPTOR_ATTR_ID_SERVERLIST,
63                CLUSTER_DESCRIPTOR_ATTR_ID_CLIENTLIST,
64                CLUSTER_DESCRIPTOR_ATTR_ID_PARTSLIST,
65            ],
66            &[],
67            &[],
68        )?;
69
70        let vendor_id = self.config.vendor_id;
71        let product_id = self.config.product_id;
72        self.set_attribute_u16(
73            0,
74            CLUSTER_ID_BASIC_INFORMATION,
75            CLUSTER_BASIC_INFORMATION_ATTR_ID_DATAMODELREVISION,
76            1,
77        );
78        let vendor_name = self.config.vendor_name.clone();
79        self.set_attribute_string(
80            0,
81            CLUSTER_ID_BASIC_INFORMATION,
82            CLUSTER_BASIC_INFORMATION_ATTR_ID_VENDORNAME,
83            &vendor_name,
84        );
85        self.set_attribute_u16(
86            0,
87            CLUSTER_ID_BASIC_INFORMATION,
88            CLUSTER_BASIC_INFORMATION_ATTR_ID_VENDORID,
89            vendor_id,
90        );
91        let product_name = self.config.product_name.clone();
92        self.set_attribute_string(
93            0,
94            CLUSTER_ID_BASIC_INFORMATION,
95            CLUSTER_BASIC_INFORMATION_ATTR_ID_PRODUCTNAME,
96            &product_name,
97        );
98        self.set_attribute_u16(
99            0,
100            CLUSTER_ID_BASIC_INFORMATION,
101            CLUSTER_BASIC_INFORMATION_ATTR_ID_PRODUCTID,
102            product_id,
103        );
104        let node_label = self.config.product_name.clone();
105        self.set_attribute_string(
106            0,
107            CLUSTER_ID_BASIC_INFORMATION,
108            CLUSTER_BASIC_INFORMATION_ATTR_ID_NODELABEL,
109            &node_label,
110        );
111        self.set_attribute_string(
112            0,
113            CLUSTER_ID_BASIC_INFORMATION,
114            CLUSTER_BASIC_INFORMATION_ATTR_ID_LOCATION,
115            "XX",
116        );
117        let hardware_version = self.config.hardware_version;
118        self.set_attribute_u16(
119            0,
120            CLUSTER_ID_BASIC_INFORMATION,
121            CLUSTER_BASIC_INFORMATION_ATTR_ID_HARDWAREVERSION,
122            hardware_version,
123        );
124        let hardware_version_string = format!("{}.0", hardware_version);
125        self.set_attribute_string(
126            0,
127            CLUSTER_ID_BASIC_INFORMATION,
128            CLUSTER_BASIC_INFORMATION_ATTR_ID_HARDWAREVERSIONSTRING,
129            &hardware_version_string,
130        );
131        let software_version = self.config.software_version;
132        self.set_attribute_u32(
133            0,
134            CLUSTER_ID_BASIC_INFORMATION,
135            CLUSTER_BASIC_INFORMATION_ATTR_ID_SOFTWAREVERSION,
136            software_version,
137        );
138        let software_version_string = software_version.to_string();
139        self.set_attribute_string(
140            0,
141            CLUSTER_ID_BASIC_INFORMATION,
142            CLUSTER_BASIC_INFORMATION_ATTR_ID_SOFTWAREVERSIONSTRING,
143            &software_version_string,
144        );
145        let serial_number = self.config.serial_number.clone();
146        self.set_attribute_string(
147            0,
148            CLUSTER_ID_BASIC_INFORMATION,
149            CLUSTER_BASIC_INFORMATION_ATTR_ID_SERIALNUMBER,
150            &serial_number,
151        );
152        self.set_attribute_bool(
153            0,
154            CLUSTER_ID_BASIC_INFORMATION,
155            CLUSTER_BASIC_INFORMATION_ATTR_ID_LOCALCONFIGDISABLED,
156            false,
157        );
158        self.set_attribute_bool(
159            0,
160            CLUSTER_ID_BASIC_INFORMATION,
161            CLUSTER_BASIC_INFORMATION_ATTR_ID_REACHABLE,
162            true,
163        );
164        let unique_id = self.config.unique_id.clone();
165        self.set_attribute_string(
166            0,
167            CLUSTER_ID_BASIC_INFORMATION,
168            CLUSTER_BASIC_INFORMATION_ATTR_ID_UNIQUEID,
169            &unique_id,
170        );
171
172        let mut buf = tlv::TlvBuffer::new();
173        buf.write_struct(2)?;
174        buf.write_uint16(0, 3)?; // CaseSessionsPerFabric
175        buf.write_uint16(1, 3)?; // SubscriptionsPerFabric
176        buf.write_struct_end()?;
177        self.set_attribute_raw(
178            0,
179            CLUSTER_ID_BASIC_INFORMATION,
180            CLUSTER_BASIC_INFORMATION_ATTR_ID_CAPABILITYMINIMA,
181            &buf.data,
182        );
183
184        self.set_attribute_u32(
185            0,
186            CLUSTER_ID_BASIC_INFORMATION,
187            CLUSTER_BASIC_INFORMATION_ATTR_ID_SPECIFICATIONVERSION,
188            0x01030000, // Matter 1.3
189        );
190        self.set_attribute_u16(
191            0,
192            CLUSTER_ID_BASIC_INFORMATION,
193            CLUSTER_BASIC_INFORMATION_ATTR_ID_MAXPATHSPERINVOKE,
194            1,
195        );
196
197        self.set_cluster_globals(
198            0,
199            CLUSTER_ID_BASIC_INFORMATION,
200            4,
201            0,
202            &[
203                CLUSTER_BASIC_INFORMATION_ATTR_ID_DATAMODELREVISION,
204                CLUSTER_BASIC_INFORMATION_ATTR_ID_VENDORNAME,
205                CLUSTER_BASIC_INFORMATION_ATTR_ID_VENDORID,
206                CLUSTER_BASIC_INFORMATION_ATTR_ID_PRODUCTNAME,
207                CLUSTER_BASIC_INFORMATION_ATTR_ID_PRODUCTID,
208                CLUSTER_BASIC_INFORMATION_ATTR_ID_NODELABEL,
209                CLUSTER_BASIC_INFORMATION_ATTR_ID_LOCATION,
210                CLUSTER_BASIC_INFORMATION_ATTR_ID_HARDWAREVERSION,
211                CLUSTER_BASIC_INFORMATION_ATTR_ID_HARDWAREVERSIONSTRING,
212                CLUSTER_BASIC_INFORMATION_ATTR_ID_SOFTWAREVERSION,
213                CLUSTER_BASIC_INFORMATION_ATTR_ID_SOFTWAREVERSIONSTRING,
214                CLUSTER_BASIC_INFORMATION_ATTR_ID_SERIALNUMBER,
215                CLUSTER_BASIC_INFORMATION_ATTR_ID_LOCALCONFIGDISABLED,
216                CLUSTER_BASIC_INFORMATION_ATTR_ID_REACHABLE,
217                CLUSTER_BASIC_INFORMATION_ATTR_ID_UNIQUEID,
218                CLUSTER_BASIC_INFORMATION_ATTR_ID_CAPABILITYMINIMA,
219                CLUSTER_BASIC_INFORMATION_ATTR_ID_SPECIFICATIONVERSION,
220                CLUSTER_BASIC_INFORMATION_ATTR_ID_MAXPATHSPERINVOKE,
221            ],
222            &[],
223            &[],
224        )?;
225
226        self.set_attribute_u64(
227            0,
228            CLUSTER_ID_GENERAL_COMMISSIONING,
229            CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BREADCRUMB,
230            0,
231        );
232
233        let mut bci_tlv = tlv::TlvBuffer::new();
234        bci_tlv.write_struct(2)?;
235        bci_tlv.write_uint16(0, 100)?;
236        bci_tlv.write_uint16(1, 200)?;
237        bci_tlv.write_struct_end()?;
238        self.set_attribute_raw(
239            0,
240            CLUSTER_ID_GENERAL_COMMISSIONING,
241            CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BASICCOMMISSIONINGINFO,
242            &bci_tlv.data,
243        );
244
245        self.set_attribute_u8(
246            0,
247            CLUSTER_ID_GENERAL_COMMISSIONING,
248            CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_REGULATORYCONFIG,
249            0,
250        );
251        self.set_attribute_u8(
252            0,
253            CLUSTER_ID_GENERAL_COMMISSIONING,
254            CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_LOCATIONCAPABILITY,
255            0,
256        );
257        self.set_attribute_bool(
258            0,
259            CLUSTER_ID_GENERAL_COMMISSIONING,
260            CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_SUPPORTSCONCURRENTCONNECTION,
261            true,
262        );
263        self.set_attribute_bool(
264            0,
265            CLUSTER_ID_GENERAL_COMMISSIONING,
266            CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_ISCOMMISSIONINGWITHOUTPOWER,
267            false,
268        );
269
270        self.set_cluster_globals(
271            0,
272            CLUSTER_ID_GENERAL_COMMISSIONING,
273            1,
274            0,
275            &[
276                CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BREADCRUMB,
277                CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_BASICCOMMISSIONINGINFO,
278                CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_REGULATORYCONFIG,
279                CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_LOCATIONCAPABILITY,
280                CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_SUPPORTSCONCURRENTCONNECTION,
281                CLUSTER_GENERAL_COMMISSIONING_ATTR_ID_ISCOMMISSIONINGWITHOUTPOWER,
282            ],
283            &[
284                CLUSTER_GENERAL_COMMISSIONING_CMD_ID_ARMFAILSAFE,
285                CLUSTER_GENERAL_COMMISSIONING_CMD_ID_SETREGULATORYCONFIG,
286                CLUSTER_GENERAL_COMMISSIONING_CMD_ID_COMMISSIONINGCOMPLETE,
287            ],
288            &[
289                CLUSTER_GENERAL_COMMISSIONING_CMD_ID_ARMFAILSAFERESPONSE,
290                CLUSTER_GENERAL_COMMISSIONING_CMD_ID_SETREGULATORYCONFIGRESPONSE,
291                CLUSTER_GENERAL_COMMISSIONING_CMD_ID_COMMISSIONINGCOMPLETERESPONSE,
292            ],
293        )?;
294
295        self.set_attribute_u8(
296            0,
297            CLUSTER_ID_OPERATIONAL_CREDENTIALS,
298            CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_SUPPORTEDFABRICS,
299            8,
300        );
301        self.set_attribute_u8(
302            0,
303            CLUSTER_ID_OPERATIONAL_CREDENTIALS,
304            CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_COMMISSIONEDFABRICS,
305            1,
306        );
307        self.set_empty_array(
308            0,
309            CLUSTER_ID_OPERATIONAL_CREDENTIALS,
310            CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_FABRICS,
311        );
312
313        self.set_cluster_globals(
314            0,
315            CLUSTER_ID_OPERATIONAL_CREDENTIALS,
316            1,
317            0,
318            &[
319                CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_NOCS,
320                CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_FABRICS,
321                CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_SUPPORTEDFABRICS,
322                CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_COMMISSIONEDFABRICS,
323                CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_TRUSTEDROOTCERTIFICATES,
324                CLUSTER_OPERATIONAL_CREDENTIALS_ATTR_ID_CURRENTFABRICINDEX,
325            ],
326            &[
327                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ATTESTATIONREQUEST,
328                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_CERTIFICATECHAINREQUEST,
329                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_CSRREQUEST,
330                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ADDTRUSTEDROOTCERTIFICATE,
331                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ADDNOC,
332                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_REMOVEFABRIC,
333            ],
334            &[
335                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_ATTESTATIONRESPONSE,
336                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_CERTIFICATECHAINRESPONSE,
337                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_CSRRESPONSE,
338                CLUSTER_OPERATIONAL_CREDENTIALS_CMD_ID_NOCRESPONSE,
339            ],
340        )?;
341
342
343        self.set_attribute_u8(
344            0,
345            CLUSTER_ID_NETWORK_COMMISSIONING,
346            CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_MAXNETWORKS,
347            1,
348        );
349        self.set_empty_array(
350            0,
351            CLUSTER_ID_NETWORK_COMMISSIONING,
352            CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_NETWORKS,
353        );
354        self.set_attribute_bool(
355            0,
356            CLUSTER_ID_NETWORK_COMMISSIONING,
357            CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_INTERFACEENABLED,
358            true,
359        );
360        self.set_attribute_u8(
361            0,
362            CLUSTER_ID_NETWORK_COMMISSIONING,
363            CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_CONNECTMAXTIMESECONDS,
364            120,
365        );
366        self.set_attribute_u8(
367            0,
368            CLUSTER_ID_NETWORK_COMMISSIONING,
369            CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKINGSTATUS,
370            0,
371        );
372        self.set_attribute_u16(
373            0,
374            CLUSTER_ID_NETWORK_COMMISSIONING,
375            CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDTHREADFEATURES,
376            0,
377        );
378
379        self.set_cluster_globals(
380            0,
381            CLUSTER_ID_NETWORK_COMMISSIONING,
382            1,
383            0x04, // FeatureMap = Ethernet
384            &[
385                CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_MAXNETWORKS,
386                CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_NETWORKS,
387                CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_CONNECTMAXTIMESECONDS,
388                CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_INTERFACEENABLED,
389                CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_LASTNETWORKINGSTATUS,
390                CLUSTER_NETWORK_COMMISSIONING_ATTR_ID_SUPPORTEDTHREADFEATURES,
391            ],
392            &[],
393            &[],
394        )?;
395
396        self.set_empty_array(
397            0,
398            CLUSTER_ID_ACCESS_CONTROL,
399            CLUSTER_ACCESS_CONTROL_ATTR_ID_ACL,
400        );
401        self.set_attribute_u16(
402            0,
403            CLUSTER_ID_ACCESS_CONTROL,
404            CLUSTER_ACCESS_CONTROL_ATTR_ID_SUBJECTSPERACCESSCONTROLENTRY,
405            4,
406        );
407        self.set_attribute_u16(
408            0,
409            CLUSTER_ID_ACCESS_CONTROL,
410            CLUSTER_ACCESS_CONTROL_ATTR_ID_TARGETSPERACCESSCONTROLENTRY,
411            3,
412        );
413        self.set_attribute_u16(
414            0,
415            CLUSTER_ID_ACCESS_CONTROL,
416            CLUSTER_ACCESS_CONTROL_ATTR_ID_ACCESSCONTROLENTRIESPERFABRIC,
417            4,
418        );
419
420        self.set_cluster_globals(
421            0,
422            CLUSTER_ID_ACCESS_CONTROL,
423            2,
424            0,
425            &[
426                CLUSTER_ACCESS_CONTROL_ATTR_ID_ACL,
427                CLUSTER_ACCESS_CONTROL_ATTR_ID_SUBJECTSPERACCESSCONTROLENTRY,
428                CLUSTER_ACCESS_CONTROL_ATTR_ID_TARGETSPERACCESSCONTROLENTRY,
429                CLUSTER_ACCESS_CONTROL_ATTR_ID_ACCESSCONTROLENTRIESPERFABRIC,
430            ],
431            &[],
432            &[],
433        )?;
434
435        self.set_attribute_u8(
436            0,
437            CLUSTER_ID_ADMINISTRATOR_COMMISSIONING,
438            CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_WINDOWSTATUS,
439            0, // WindowNotOpen
440        );
441        self.set_attribute_u8(
442            0,
443            CLUSTER_ID_ADMINISTRATOR_COMMISSIONING,
444            CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_ADMINFABRICINDEX,
445            0,
446        );
447        self.set_attribute_u16(
448            0,
449            CLUSTER_ID_ADMINISTRATOR_COMMISSIONING,
450            CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_ADMINVENDORID,
451            0,
452        );
453
454        self.set_cluster_globals(
455            0,
456            CLUSTER_ID_ADMINISTRATOR_COMMISSIONING,
457            1,
458            0,
459            &[
460                CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_WINDOWSTATUS,
461                CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_ADMINFABRICINDEX,
462                CLUSTER_ADMINISTRATOR_COMMISSIONING_ATTR_ID_ADMINVENDORID,
463            ],
464            &[
465                CLUSTER_ADMINISTRATOR_COMMISSIONING_CMD_ID_OPENCOMMISSIONINGWINDOW,
466                CLUSTER_ADMINISTRATOR_COMMISSIONING_CMD_ID_OPENBASICCOMMISSIONINGWINDOW,
467                CLUSTER_ADMINISTRATOR_COMMISSIONING_CMD_ID_REVOKECOMMISSIONING,
468            ],
469            &[],
470        )?;
471
472        // WiFi Network Diagnostics cluster (0x60), attr 1 (kept for compat)
473        self.set_attribute_u8(0, 0x60, 1, 3u8);
474
475        Ok(())
476    }
477
478    pub fn set_empty_array(&mut self, endpoint: u16, cluster: u32, attribute: u32) {
479        let mut buf = tlv::TlvBuffer::new();
480        let _ = buf.write_array(2);
481        let _ = buf.write_struct_end();
482        self.set_attribute_raw(endpoint, cluster, attribute, &buf.data);
483    }
484
485    pub fn set_cluster_globals(
486        &mut self,
487        endpoint: u16,
488        cluster: u32,
489        revision: u16,
490        feature_map: u32,
491        attribute_ids: &[u32],
492        accepted_cmds: &[u32],
493        generated_cmds: &[u32],
494    ) -> Result<()> {
495        self.set_attribute_u16(endpoint, cluster, ATTR_CLUSTER_REVISION, revision);
496        self.set_attribute_u32(endpoint, cluster, ATTR_FEATURE_MAP, feature_map);
497
498        let mut buf = tlv::TlvBuffer::new();
499        buf.write_array(2)?;
500        for &id in attribute_ids {
501            buf.write_uint32_notag(id)?;
502        }
503        buf.write_uint32_notag(ATTR_ATTRIBUTE_LIST)?;
504        buf.write_uint32_notag(ATTR_FEATURE_MAP)?;
505        buf.write_uint32_notag(ATTR_CLUSTER_REVISION)?;
506        buf.write_uint32_notag(ATTR_EVENT_LIST)?;
507        buf.write_uint32_notag(ATTR_ACCEPTED_CMD_LIST)?;
508        buf.write_uint32_notag(ATTR_GENERATED_CMD_LIST)?;
509        buf.write_struct_end()?;
510        self.set_attribute_raw(endpoint, cluster, ATTR_ATTRIBUTE_LIST, &buf.data);
511
512        self.set_empty_array(endpoint, cluster, ATTR_EVENT_LIST);
513
514        let mut buf = tlv::TlvBuffer::new();
515        buf.write_array(2)?;
516        for &cmd in accepted_cmds {
517            buf.write_uint32_notag(cmd)?;
518        }
519        buf.write_struct_end()?;
520        self.set_attribute_raw(endpoint, cluster, ATTR_ACCEPTED_CMD_LIST, &buf.data);
521
522        let mut buf = tlv::TlvBuffer::new();
523        buf.write_array(2)?;
524        for &cmd in generated_cmds {
525            buf.write_uint32_notag(cmd)?;
526        }
527        buf.write_struct_end()?;
528        self.set_attribute_raw(endpoint, cluster, ATTR_GENERATED_CMD_LIST, &buf.data);
529
530        Ok(())
531    }
532
533    pub fn set_attribute_bool(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: bool) {
534        let mut buf = tlv::TlvBuffer::new();
535        let _ = buf.write_bool(2, value);
536        self.attributes
537            .insert((endpoint, cluster, attribute), buf.data);
538        self.dirty_attributes.insert((endpoint, cluster, attribute));
539    }
540
541    pub fn set_attribute_u8(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u8) {
542        let mut buf = tlv::TlvBuffer::new();
543        let _ = buf.write_uint8(2, value);
544        self.attributes
545            .insert((endpoint, cluster, attribute), buf.data);
546        self.dirty_attributes.insert((endpoint, cluster, attribute));
547    }
548
549    pub fn set_attribute_u16(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u16) {
550        let mut buf = tlv::TlvBuffer::new();
551        let _ = buf.write_uint16(2, value);
552        self.attributes
553            .insert((endpoint, cluster, attribute), buf.data);
554        self.dirty_attributes.insert((endpoint, cluster, attribute));
555    }
556
557    pub fn set_attribute_u32(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u32) {
558        let mut buf = tlv::TlvBuffer::new();
559        let _ = buf.write_uint32(2, value);
560        self.attributes
561            .insert((endpoint, cluster, attribute), buf.data);
562        self.dirty_attributes.insert((endpoint, cluster, attribute));
563    }
564
565    pub fn set_attribute_u64(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u64) {
566        let mut buf = tlv::TlvBuffer::new();
567        let _ = buf.write_uint64(2, value);
568        self.attributes
569            .insert((endpoint, cluster, attribute), buf.data);
570        self.dirty_attributes.insert((endpoint, cluster, attribute));
571    }
572
573    pub fn set_attribute_string(
574        &mut self,
575        endpoint: u16,
576        cluster: u32,
577        attribute: u32,
578        value: &str,
579    ) {
580        let mut buf = tlv::TlvBuffer::new();
581        let _ = buf.write_string(2, value);
582        self.attributes
583            .insert((endpoint, cluster, attribute), buf.data);
584        self.dirty_attributes.insert((endpoint, cluster, attribute));
585    }
586
587    pub fn set_attribute_raw(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: &[u8]) {
588        self.attributes
589            .insert((endpoint, cluster, attribute), value.to_vec());
590        self.dirty_attributes.insert((endpoint, cluster, attribute));
591    }
592}
593
594
595use std::collections::{HashMap, HashSet};
596
597pub fn attr_set_bool(
598    attributes: &mut HashMap<(u16, u32, u32), Vec<u8>>,
599    dirty: &mut HashSet<(u16, u32, u32)>,
600    ep: u16,
601    cluster: u32,
602    attr: u32,
603    value: bool,
604) {
605    let mut buf = tlv::TlvBuffer::new();
606    let _ = buf.write_bool(2, value);
607    attributes.insert((ep, cluster, attr), buf.data);
608    dirty.insert((ep, cluster, attr));
609}
610
611pub fn attr_get_bool(
612    attributes: &HashMap<(u16, u32, u32), Vec<u8>>,
613    ep: u16,
614    cluster: u32,
615    attr: u32,
616) -> Option<bool> {
617    attributes
618        .get(&(ep, cluster, attr))
619        .and_then(|v| tlv::decode_tlv(v).ok())
620        .map(|item| bool::from(item.value))
621}