matc/device/
attributes.rs

1use anyhow::{bail, 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    #[allow(clippy::too_many_arguments)]
486    pub fn set_cluster_globals(
487        &mut self,
488        endpoint: u16,
489        cluster: u32,
490        revision: u16,
491        feature_map: u32,
492        attribute_ids: &[u32],
493        accepted_cmds: &[u32],
494        generated_cmds: &[u32],
495    ) -> Result<()> {
496        self.set_attribute_u16(endpoint, cluster, ATTR_CLUSTER_REVISION, revision);
497        self.set_attribute_u32(endpoint, cluster, ATTR_FEATURE_MAP, feature_map);
498
499        let mut buf = tlv::TlvBuffer::new();
500        buf.write_array(2)?;
501        for &id in attribute_ids {
502            buf.write_uint32_notag(id)?;
503        }
504        buf.write_uint32_notag(ATTR_ATTRIBUTE_LIST)?;
505        buf.write_uint32_notag(ATTR_FEATURE_MAP)?;
506        buf.write_uint32_notag(ATTR_CLUSTER_REVISION)?;
507        buf.write_uint32_notag(ATTR_EVENT_LIST)?;
508        buf.write_uint32_notag(ATTR_ACCEPTED_CMD_LIST)?;
509        buf.write_uint32_notag(ATTR_GENERATED_CMD_LIST)?;
510        buf.write_struct_end()?;
511        self.set_attribute_raw(endpoint, cluster, ATTR_ATTRIBUTE_LIST, &buf.data);
512
513        self.set_empty_array(endpoint, cluster, ATTR_EVENT_LIST);
514
515        let mut buf = tlv::TlvBuffer::new();
516        buf.write_array(2)?;
517        for &cmd in accepted_cmds {
518            buf.write_uint32_notag(cmd)?;
519        }
520        buf.write_struct_end()?;
521        self.set_attribute_raw(endpoint, cluster, ATTR_ACCEPTED_CMD_LIST, &buf.data);
522
523        let mut buf = tlv::TlvBuffer::new();
524        buf.write_array(2)?;
525        for &cmd in generated_cmds {
526            buf.write_uint32_notag(cmd)?;
527        }
528        buf.write_struct_end()?;
529        self.set_attribute_raw(endpoint, cluster, ATTR_GENERATED_CMD_LIST, &buf.data);
530
531        Ok(())
532    }
533
534    pub fn set_attribute_bool(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: bool) {
535        let mut buf = tlv::TlvBuffer::new();
536        let _ = buf.write_bool(2, value);
537        self.attributes
538            .insert((endpoint, cluster, attribute), buf.data);
539        self.dirty_attributes.insert((endpoint, cluster, attribute));
540    }
541
542    pub fn set_attribute_u8(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u8) {
543        let mut buf = tlv::TlvBuffer::new();
544        let _ = buf.write_uint8(2, value);
545        self.attributes
546            .insert((endpoint, cluster, attribute), buf.data);
547        self.dirty_attributes.insert((endpoint, cluster, attribute));
548    }
549
550    pub fn set_attribute_u16(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u16) {
551        let mut buf = tlv::TlvBuffer::new();
552        let _ = buf.write_uint16(2, value);
553        self.attributes
554            .insert((endpoint, cluster, attribute), buf.data);
555        self.dirty_attributes.insert((endpoint, cluster, attribute));
556    }
557
558    pub fn set_attribute_u32(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u32) {
559        let mut buf = tlv::TlvBuffer::new();
560        let _ = buf.write_uint32(2, value);
561        self.attributes
562            .insert((endpoint, cluster, attribute), buf.data);
563        self.dirty_attributes.insert((endpoint, cluster, attribute));
564    }
565
566    pub fn set_attribute_u64(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: u64) {
567        let mut buf = tlv::TlvBuffer::new();
568        let _ = buf.write_uint64(2, value);
569        self.attributes
570            .insert((endpoint, cluster, attribute), buf.data);
571        self.dirty_attributes.insert((endpoint, cluster, attribute));
572    }
573
574    pub fn set_attribute_string(
575        &mut self,
576        endpoint: u16,
577        cluster: u32,
578        attribute: u32,
579        value: &str,
580    ) {
581        let mut buf = tlv::TlvBuffer::new();
582        let _ = buf.write_string(2, value);
583        self.attributes
584            .insert((endpoint, cluster, attribute), buf.data);
585        self.dirty_attributes.insert((endpoint, cluster, attribute));
586    }
587
588    pub fn set_attribute_raw(&mut self, endpoint: u16, cluster: u32, attribute: u32, value: &[u8]) {
589        self.attributes
590            .insert((endpoint, cluster, attribute), value.to_vec());
591        self.dirty_attributes.insert((endpoint, cluster, attribute));
592    }
593}
594
595
596use std::collections::{HashMap, HashSet};
597
598/// Context passed to [`super::AppHandler::handle_command`] giving typed attribute access.
599pub struct AttrContext<'a> {
600    pub(crate) attributes: &'a mut HashMap<(u16, u32, u32), Vec<u8>>,
601    pub(crate) dirty: &'a mut HashSet<(u16, u32, u32)>,
602}
603
604impl<'a> AttrContext<'a> {
605    pub fn get_bool(&self, ep: u16, cluster: u32, attr: u32) -> Option<bool> {
606        self.attributes.get(&(ep, cluster, attr))
607            .and_then(|v| tlv::decode_tlv(v).ok())
608            .and_then(|item| item.get_bool(&[]))
609    }
610    pub fn get_u8(&self, ep: u16, cluster: u32, attr: u32) -> Option<u8> {
611        self.attributes.get(&(ep, cluster, attr))
612            .and_then(|v| tlv::decode_tlv(v).ok())
613            .and_then(|item| item.get_u8(&[]))
614    }
615    pub fn get_u16(&self, ep: u16, cluster: u32, attr: u32) -> Option<u16> {
616        self.attributes.get(&(ep, cluster, attr))
617            .and_then(|v| tlv::decode_tlv(v).ok())
618            .and_then(|item| item.get_u16(&[]))
619    }
620    pub fn get_u32(&self, ep: u16, cluster: u32, attr: u32) -> Option<u32> {
621        self.attributes.get(&(ep, cluster, attr))
622            .and_then(|v| tlv::decode_tlv(v).ok())
623            .and_then(|item| item.get_u32(&[]))
624    }
625    pub fn get_u64(&self, ep: u16, cluster: u32, attr: u32) -> Option<u64> {
626        self.attributes.get(&(ep, cluster, attr))
627            .and_then(|v| tlv::decode_tlv(v).ok())
628            .and_then(|item| item.get_u64(&[]))
629    }
630    pub fn set_bool(&mut self, ep: u16, cluster: u32, attr: u32, value: bool) {
631        let mut buf = tlv::TlvBuffer::new();
632        let _ = buf.write_bool(2, value);
633        self.attributes.insert((ep, cluster, attr), buf.data);
634        self.dirty.insert((ep, cluster, attr));
635    }
636    pub fn set_u8(&mut self, ep: u16, cluster: u32, attr: u32, value: u8) {
637        let mut buf = tlv::TlvBuffer::new();
638        let _ = buf.write_uint8(2, value);
639        self.attributes.insert((ep, cluster, attr), buf.data);
640        self.dirty.insert((ep, cluster, attr));
641    }
642    pub fn set_u16(&mut self, ep: u16, cluster: u32, attr: u32, value: u16) {
643        let mut buf = tlv::TlvBuffer::new();
644        let _ = buf.write_uint16(2, value);
645        self.attributes.insert((ep, cluster, attr), buf.data);
646        self.dirty.insert((ep, cluster, attr));
647    }
648    pub fn set_u32(&mut self, ep: u16, cluster: u32, attr: u32, value: u32) {
649        let mut buf = tlv::TlvBuffer::new();
650        let _ = buf.write_uint32(2, value);
651        self.attributes.insert((ep, cluster, attr), buf.data);
652        self.dirty.insert((ep, cluster, attr));
653    }
654    pub fn set_u64(&mut self, ep: u16, cluster: u32, attr: u32, value: u64) {
655        let mut buf = tlv::TlvBuffer::new();
656        let _ = buf.write_uint64(2, value);
657        self.attributes.insert((ep, cluster, attr), buf.data);
658        self.dirty.insert((ep, cluster, attr));
659    }
660    pub fn set_string(&mut self, ep: u16, cluster: u32, attr: u32, value: &str) {
661        let mut buf = tlv::TlvBuffer::new();
662        let _ = buf.write_string(2, value);
663        self.attributes.insert((ep, cluster, attr), buf.data);
664        self.dirty.insert((ep, cluster, attr));
665    }
666}
667
668impl Device {
669    /// Register a new application endpoint with a Descriptor cluster.
670    ///
671    /// Sets up DeviceTypeList, ServerList (initially containing only Descriptor),
672    /// empty ClientList and PartsList, and Descriptor globals. Then appends the
673    /// endpoint to EP0's PartsList.
674    pub fn add_endpoint(
675        &mut self,
676        endpoint: u16,
677        device_type_id: u32,
678        device_type_revision: u16,
679    ) -> Result<()> {
680        use clusters::defs::*;
681        if self.endpoints.contains(&endpoint) {
682            bail!("endpoint {} already registered", endpoint);
683        }
684
685        let mut buf = tlv::TlvBuffer::new();
686        buf.write_array(2)?;
687        buf.write_anon_struct()?;
688        buf.write_uint32(0, device_type_id)?;
689        buf.write_uint16(1, device_type_revision)?;
690        buf.write_struct_end()?;
691        buf.write_struct_end()?;
692        self.set_attribute_raw(endpoint, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_DEVICETYPELIST, &buf.data);
693
694        let mut buf = tlv::TlvBuffer::new();
695        buf.write_array(2)?;
696        buf.write_uint32_notag(CLUSTER_ID_DESCRIPTOR)?;
697        buf.write_struct_end()?;
698        self.set_attribute_raw(endpoint, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_SERVERLIST, &buf.data);
699
700        self.set_empty_array(endpoint, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_CLIENTLIST);
701        self.set_empty_array(endpoint, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_PARTSLIST);
702
703        self.set_cluster_globals(
704            endpoint,
705            CLUSTER_ID_DESCRIPTOR,
706            3,
707            0,
708            &[
709                CLUSTER_DESCRIPTOR_ATTR_ID_DEVICETYPELIST,
710                CLUSTER_DESCRIPTOR_ATTR_ID_SERVERLIST,
711                CLUSTER_DESCRIPTOR_ATTR_ID_CLIENTLIST,
712                CLUSTER_DESCRIPTOR_ATTR_ID_PARTSLIST,
713            ],
714            &[],
715            &[],
716        )?;
717
718        self.endpoints.push(endpoint);
719        self.rebuild_ep0_partslist()?;
720        Ok(())
721    }
722
723    /// Add a cluster to an existing endpoint: sets cluster globals and updates
724    /// the endpoint's Descriptor ServerList.
725    #[allow(clippy::too_many_arguments)]
726    pub fn add_cluster(
727        &mut self,
728        endpoint: u16,
729        cluster_id: u32,
730        revision: u16,
731        feature_map: u32,
732        attr_ids: &[u32],
733        accepted_cmds: &[u32],
734        generated_cmds: &[u32],
735    ) -> Result<()> {
736        if !self.endpoints.contains(&endpoint) {
737            bail!("endpoint {} not registered; call add_endpoint first", endpoint);
738        }
739        self.set_cluster_globals(endpoint, cluster_id, revision, feature_map, attr_ids, accepted_cmds, generated_cmds)?;
740        self.rebuild_server_list(endpoint, cluster_id)?;
741        Ok(())
742    }
743
744    /// Rebuild EP0 Descriptor PartsList from the current `self.endpoints` (excluding EP0).
745    fn rebuild_ep0_partslist(&mut self) -> Result<()> {
746        use clusters::defs::*;
747        let mut buf = tlv::TlvBuffer::new();
748        buf.write_array(2)?;
749        for &ep in &self.endpoints {
750            if ep != 0 {
751                buf.write_uint16_notag(ep)?;
752            }
753        }
754        buf.write_struct_end()?;
755        self.set_attribute_raw(0, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_PARTSLIST, &buf.data);
756        Ok(())
757    }
758
759    /// Append `cluster_id` to the Descriptor ServerList for `endpoint`.
760    fn rebuild_server_list(&mut self, endpoint: u16, cluster_id: u32) -> Result<()> {
761        use clusters::defs::*;
762        let existing: Vec<u32> = self.attributes
763            .get(&(endpoint, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_SERVERLIST))
764            .and_then(|data| tlv::decode_tlv(data).ok())
765            .map(|item| {
766                if let tlv::TlvItemValue::List(entries) = item.value {
767                    entries.into_iter()
768                        .filter_map(|e| if let tlv::TlvItemValue::Int(i) = e.value { Some(i as u32) } else { None })
769                        .collect()
770                } else {
771                    vec![]
772                }
773            })
774            .unwrap_or_default();
775
776        let mut buf = tlv::TlvBuffer::new();
777        buf.write_array(2)?;
778        for id in existing {
779            buf.write_uint32_notag(id)?;
780        }
781        buf.write_uint32_notag(cluster_id)?;
782        buf.write_struct_end()?;
783        self.set_attribute_raw(endpoint, CLUSTER_ID_DESCRIPTOR, CLUSTER_DESCRIPTOR_ATTR_ID_SERVERLIST, &buf.data);
784        Ok(())
785    }
786}
787