matc/clusters/codec/
door_lock.rs

1//! Matter TLV encoders and decoders for Door Lock Cluster
2//! Cluster ID: 0x0101
3//!
4//! This file is automatically generated from DoorLock.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Import serialization helpers for octet strings
12use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14// Enum definitions
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum AlarmCode {
19    /// Locking Mechanism Jammed
20    Lockjammed = 0,
21    /// Lock Reset to Factory Defaults
22    Lockfactoryreset = 1,
23    /// Lock Radio Power Cycled
24    Lockradiopowercycled = 3,
25    /// Tamper Alarm - wrong code entry limit
26    Wrongcodeentrylimit = 4,
27    /// Tamper Alarm - front escutcheon removed from main
28    Frontesceutcheonremoved = 5,
29    /// Forced Door Open under Door Locked Condition
30    Doorforcedopen = 6,
31    /// Door ajar
32    Doorajar = 7,
33    /// Force User SOS alarm
34    Forceduser = 8,
35}
36
37impl AlarmCode {
38    /// Convert from u8 value
39    pub fn from_u8(value: u8) -> Option<Self> {
40        match value {
41            0 => Some(AlarmCode::Lockjammed),
42            1 => Some(AlarmCode::Lockfactoryreset),
43            3 => Some(AlarmCode::Lockradiopowercycled),
44            4 => Some(AlarmCode::Wrongcodeentrylimit),
45            5 => Some(AlarmCode::Frontesceutcheonremoved),
46            6 => Some(AlarmCode::Doorforcedopen),
47            7 => Some(AlarmCode::Doorajar),
48            8 => Some(AlarmCode::Forceduser),
49            _ => None,
50        }
51    }
52
53    /// Convert to u8 value
54    pub fn to_u8(self) -> u8 {
55        self as u8
56    }
57}
58
59impl From<AlarmCode> for u8 {
60    fn from(val: AlarmCode) -> Self {
61        val as u8
62    }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
66#[repr(u8)]
67pub enum CredentialRule {
68    /// Only one credential is required for lock operation
69    Single = 0,
70    /// Any two credentials are required for lock operation
71    Dual = 1,
72    /// Any three credentials are required for lock operation
73    Tri = 2,
74}
75
76impl CredentialRule {
77    /// Convert from u8 value
78    pub fn from_u8(value: u8) -> Option<Self> {
79        match value {
80            0 => Some(CredentialRule::Single),
81            1 => Some(CredentialRule::Dual),
82            2 => Some(CredentialRule::Tri),
83            _ => None,
84        }
85    }
86
87    /// Convert to u8 value
88    pub fn to_u8(self) -> u8 {
89        self as u8
90    }
91}
92
93impl From<CredentialRule> for u8 {
94    fn from(val: CredentialRule) -> Self {
95        val as u8
96    }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
100#[repr(u8)]
101pub enum CredentialType {
102    /// Programming PIN code credential type
103    Programmingpin = 0,
104    /// PIN code credential type
105    Pin = 1,
106    /// RFID identifier credential type
107    Rfid = 2,
108    /// Fingerprint identifier credential type
109    Fingerprint = 3,
110    /// Finger vein identifier credential type
111    Fingervein = 4,
112    /// Face identifier credential type
113    Face = 5,
114    /// A Credential Issuer public key as defined in Aliro
115    Alirocredentialissuerkey = 6,
116    /// An Endpoint public key as defined in Aliro which can be evicted if space is needed for another endpoint key
117    Aliroevictableendpointkey = 7,
118    /// An Endpoint public key as defined in Aliro which cannot be evicted if space is needed for another endpoint key
119    Alirononevictableendpointkey = 8,
120}
121
122impl CredentialType {
123    /// Convert from u8 value
124    pub fn from_u8(value: u8) -> Option<Self> {
125        match value {
126            0 => Some(CredentialType::Programmingpin),
127            1 => Some(CredentialType::Pin),
128            2 => Some(CredentialType::Rfid),
129            3 => Some(CredentialType::Fingerprint),
130            4 => Some(CredentialType::Fingervein),
131            5 => Some(CredentialType::Face),
132            6 => Some(CredentialType::Alirocredentialissuerkey),
133            7 => Some(CredentialType::Aliroevictableendpointkey),
134            8 => Some(CredentialType::Alirononevictableendpointkey),
135            _ => None,
136        }
137    }
138
139    /// Convert to u8 value
140    pub fn to_u8(self) -> u8 {
141        self as u8
142    }
143}
144
145impl From<CredentialType> for u8 {
146    fn from(val: CredentialType) -> Self {
147        val as u8
148    }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
152#[repr(u8)]
153pub enum DataOperationType {
154    /// Data is being added or was added
155    Add = 0,
156    /// Data is being cleared or was cleared
157    Clear = 1,
158    /// Data is being modified or was modified
159    Modify = 2,
160}
161
162impl DataOperationType {
163    /// Convert from u8 value
164    pub fn from_u8(value: u8) -> Option<Self> {
165        match value {
166            0 => Some(DataOperationType::Add),
167            1 => Some(DataOperationType::Clear),
168            2 => Some(DataOperationType::Modify),
169            _ => None,
170        }
171    }
172
173    /// Convert to u8 value
174    pub fn to_u8(self) -> u8 {
175        self as u8
176    }
177}
178
179impl From<DataOperationType> for u8 {
180    fn from(val: DataOperationType) -> Self {
181        val as u8
182    }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
186#[repr(u8)]
187pub enum DoorState {
188    /// Door state is open
189    Dooropen = 0,
190    /// Door state is closed
191    Doorclosed = 1,
192    /// Door state is jammed
193    Doorjammed = 2,
194    /// Door state is currently forced open
195    Doorforcedopen = 3,
196    /// Door state is invalid for unspecified reason
197    Doorunspecifiederror = 4,
198    /// Door state is ajar
199    Doorajar = 5,
200}
201
202impl DoorState {
203    /// Convert from u8 value
204    pub fn from_u8(value: u8) -> Option<Self> {
205        match value {
206            0 => Some(DoorState::Dooropen),
207            1 => Some(DoorState::Doorclosed),
208            2 => Some(DoorState::Doorjammed),
209            3 => Some(DoorState::Doorforcedopen),
210            4 => Some(DoorState::Doorunspecifiederror),
211            5 => Some(DoorState::Doorajar),
212            _ => None,
213        }
214    }
215
216    /// Convert to u8 value
217    pub fn to_u8(self) -> u8 {
218        self as u8
219    }
220}
221
222impl From<DoorState> for u8 {
223    fn from(val: DoorState) -> Self {
224        val as u8
225    }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
229#[repr(u8)]
230pub enum EventType {
231    /// Event type is operation
232    Operation = 0,
233    /// Event type is programming
234    Programming = 1,
235    /// Event type is alarm
236    Alarm = 2,
237}
238
239impl EventType {
240    /// Convert from u8 value
241    pub fn from_u8(value: u8) -> Option<Self> {
242        match value {
243            0 => Some(EventType::Operation),
244            1 => Some(EventType::Programming),
245            2 => Some(EventType::Alarm),
246            _ => None,
247        }
248    }
249
250    /// Convert to u8 value
251    pub fn to_u8(self) -> u8 {
252        self as u8
253    }
254}
255
256impl From<EventType> for u8 {
257    fn from(val: EventType) -> Self {
258        val as u8
259    }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
263#[repr(u8)]
264pub enum LEDSetting {
265    /// Never use LED for signalization
266    Noledsignal = 0,
267    /// Use LED signalization except for access allowed events
268    Noledsignalaccessallowed = 1,
269    /// Use LED signalization for all events
270    Ledsignalall = 2,
271}
272
273impl LEDSetting {
274    /// Convert from u8 value
275    pub fn from_u8(value: u8) -> Option<Self> {
276        match value {
277            0 => Some(LEDSetting::Noledsignal),
278            1 => Some(LEDSetting::Noledsignalaccessallowed),
279            2 => Some(LEDSetting::Ledsignalall),
280            _ => None,
281        }
282    }
283
284    /// Convert to u8 value
285    pub fn to_u8(self) -> u8 {
286        self as u8
287    }
288}
289
290impl From<LEDSetting> for u8 {
291    fn from(val: LEDSetting) -> Self {
292        val as u8
293    }
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
297#[repr(u8)]
298pub enum LockDataType {
299    /// Unspecified or manufacturer specific lock user data added, cleared, or modified.
300    Unspecified = 0,
301    /// Lock programming PIN code was added, cleared, or modified.
302    Programmingcode = 1,
303    /// Lock user index was added, cleared, or modified.
304    Userindex = 2,
305    /// Lock user week day schedule was added, cleared, or modified.
306    Weekdayschedule = 3,
307    /// Lock user year day schedule was added, cleared, or modified.
308    Yeardayschedule = 4,
309    /// Lock holiday schedule was added, cleared, or modified.
310    Holidayschedule = 5,
311    /// Lock user PIN code was added, cleared, or modified.
312    Pin = 6,
313    /// Lock user RFID code was added, cleared, or modified.
314    Rfid = 7,
315    /// Lock user fingerprint was added, cleared, or modified.
316    Fingerprint = 8,
317    /// Lock user finger-vein information was added, cleared, or modified.
318    Fingervein = 9,
319    /// Lock user face information was added, cleared, or modified.
320    Face = 10,
321    /// An Aliro credential issuer key credential was added, cleared, or modified.
322    Alirocredentialissuerkey = 11,
323    /// An Aliro endpoint key credential which can be evicted credential was added, cleared, or modified.
324    Aliroevictableendpointkey = 12,
325    /// An Aliro endpoint key credential which cannot be evicted was added, cleared, or modified.
326    Alirononevictableendpointkey = 13,
327}
328
329impl LockDataType {
330    /// Convert from u8 value
331    pub fn from_u8(value: u8) -> Option<Self> {
332        match value {
333            0 => Some(LockDataType::Unspecified),
334            1 => Some(LockDataType::Programmingcode),
335            2 => Some(LockDataType::Userindex),
336            3 => Some(LockDataType::Weekdayschedule),
337            4 => Some(LockDataType::Yeardayschedule),
338            5 => Some(LockDataType::Holidayschedule),
339            6 => Some(LockDataType::Pin),
340            7 => Some(LockDataType::Rfid),
341            8 => Some(LockDataType::Fingerprint),
342            9 => Some(LockDataType::Fingervein),
343            10 => Some(LockDataType::Face),
344            11 => Some(LockDataType::Alirocredentialissuerkey),
345            12 => Some(LockDataType::Aliroevictableendpointkey),
346            13 => Some(LockDataType::Alirononevictableendpointkey),
347            _ => None,
348        }
349    }
350
351    /// Convert to u8 value
352    pub fn to_u8(self) -> u8 {
353        self as u8
354    }
355}
356
357impl From<LockDataType> for u8 {
358    fn from(val: LockDataType) -> Self {
359        val as u8
360    }
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
364#[repr(u8)]
365pub enum LockOperationType {
366    /// Lock operation
367    Lock = 0,
368    /// Unlock operation
369    Unlock = 1,
370    /// Triggered by keypad entry for user with User Type set to Non Access User
371    Nonaccessuserevent = 2,
372    /// Triggered by using a user with UserType set to Forced User
373    Forceduserevent = 3,
374    /// Unlatch operation
375    Unlatch = 4,
376}
377
378impl LockOperationType {
379    /// Convert from u8 value
380    pub fn from_u8(value: u8) -> Option<Self> {
381        match value {
382            0 => Some(LockOperationType::Lock),
383            1 => Some(LockOperationType::Unlock),
384            2 => Some(LockOperationType::Nonaccessuserevent),
385            3 => Some(LockOperationType::Forceduserevent),
386            4 => Some(LockOperationType::Unlatch),
387            _ => None,
388        }
389    }
390
391    /// Convert to u8 value
392    pub fn to_u8(self) -> u8 {
393        self as u8
394    }
395}
396
397impl From<LockOperationType> for u8 {
398    fn from(val: LockOperationType) -> Self {
399        val as u8
400    }
401}
402
403#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
404#[repr(u8)]
405pub enum LockState {
406    /// Lock state is not fully locked
407    Notfullylocked = 0,
408    /// Lock state is fully locked
409    Locked = 1,
410    /// Lock state is fully unlocked
411    Unlocked = 2,
412    /// Lock state is fully unlocked and the latch is pulled
413    Unlatched = 3,
414}
415
416impl LockState {
417    /// Convert from u8 value
418    pub fn from_u8(value: u8) -> Option<Self> {
419        match value {
420            0 => Some(LockState::Notfullylocked),
421            1 => Some(LockState::Locked),
422            2 => Some(LockState::Unlocked),
423            3 => Some(LockState::Unlatched),
424            _ => None,
425        }
426    }
427
428    /// Convert to u8 value
429    pub fn to_u8(self) -> u8 {
430        self as u8
431    }
432}
433
434impl From<LockState> for u8 {
435    fn from(val: LockState) -> Self {
436        val as u8
437    }
438}
439
440#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
441#[repr(u8)]
442pub enum LockType {
443    /// Physical lock type is dead bolt
444    Deadbolt = 0,
445    /// Physical lock type is magnetic
446    Magnetic = 1,
447    /// Physical lock type is other
448    Other = 2,
449    /// Physical lock type is mortise
450    Mortise = 3,
451    /// Physical lock type is rim
452    Rim = 4,
453    /// Physical lock type is latch bolt
454    Latchbolt = 5,
455    /// Physical lock type is cylindrical lock
456    Cylindricallock = 6,
457    /// Physical lock type is tubular lock
458    Tubularlock = 7,
459    /// Physical lock type is interconnected lock
460    Interconnectedlock = 8,
461    /// Physical lock type is dead latch
462    Deadlatch = 9,
463    /// Physical lock type is door furniture
464    Doorfurniture = 10,
465    /// Physical lock type is euro cylinder
466    Eurocylinder = 11,
467}
468
469impl LockType {
470    /// Convert from u8 value
471    pub fn from_u8(value: u8) -> Option<Self> {
472        match value {
473            0 => Some(LockType::Deadbolt),
474            1 => Some(LockType::Magnetic),
475            2 => Some(LockType::Other),
476            3 => Some(LockType::Mortise),
477            4 => Some(LockType::Rim),
478            5 => Some(LockType::Latchbolt),
479            6 => Some(LockType::Cylindricallock),
480            7 => Some(LockType::Tubularlock),
481            8 => Some(LockType::Interconnectedlock),
482            9 => Some(LockType::Deadlatch),
483            10 => Some(LockType::Doorfurniture),
484            11 => Some(LockType::Eurocylinder),
485            _ => None,
486        }
487    }
488
489    /// Convert to u8 value
490    pub fn to_u8(self) -> u8 {
491        self as u8
492    }
493}
494
495impl From<LockType> for u8 {
496    fn from(val: LockType) -> Self {
497        val as u8
498    }
499}
500
501#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
502#[repr(u8)]
503pub enum OperatingMode {
504    Normal = 0,
505    Vacation = 1,
506    Privacy = 2,
507    Noremotelockunlock = 3,
508    Passage = 4,
509}
510
511impl OperatingMode {
512    /// Convert from u8 value
513    pub fn from_u8(value: u8) -> Option<Self> {
514        match value {
515            0 => Some(OperatingMode::Normal),
516            1 => Some(OperatingMode::Vacation),
517            2 => Some(OperatingMode::Privacy),
518            3 => Some(OperatingMode::Noremotelockunlock),
519            4 => Some(OperatingMode::Passage),
520            _ => None,
521        }
522    }
523
524    /// Convert to u8 value
525    pub fn to_u8(self) -> u8 {
526        self as u8
527    }
528}
529
530impl From<OperatingMode> for u8 {
531    fn from(val: OperatingMode) -> Self {
532        val as u8
533    }
534}
535
536#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
537#[repr(u8)]
538pub enum OperationError {
539    /// Lock/unlock error caused by unknown or unspecified source
540    Unspecified = 0,
541    /// Lock/unlock error caused by invalid PIN, RFID, fingerprint or other credential
542    Invalidcredential = 1,
543    /// Lock/unlock error caused by disabled USER or credential
544    Disableduserdenied = 2,
545    /// Lock/unlock error caused by schedule restriction
546    Restricted = 3,
547    /// Lock/unlock error caused by insufficient battery power left to safely actuate the lock
548    Insufficientbattery = 4,
549}
550
551impl OperationError {
552    /// Convert from u8 value
553    pub fn from_u8(value: u8) -> Option<Self> {
554        match value {
555            0 => Some(OperationError::Unspecified),
556            1 => Some(OperationError::Invalidcredential),
557            2 => Some(OperationError::Disableduserdenied),
558            3 => Some(OperationError::Restricted),
559            4 => Some(OperationError::Insufficientbattery),
560            _ => None,
561        }
562    }
563
564    /// Convert to u8 value
565    pub fn to_u8(self) -> u8 {
566        self as u8
567    }
568}
569
570impl From<OperationError> for u8 {
571    fn from(val: OperationError) -> Self {
572        val as u8
573    }
574}
575
576#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
577#[repr(u8)]
578pub enum OperationSource {
579    /// Lock/unlock operation came from unspecified source
580    Unspecified = 0,
581    /// Lock/unlock operation came from manual operation (key, thumbturn, handle, etc).
582    Manual = 1,
583    /// Lock/unlock operation came from proprietary remote source (e.g. vendor app/cloud)
584    Proprietaryremote = 2,
585    /// Lock/unlock operation came from keypad
586    Keypad = 3,
587    /// Lock/unlock operation came from lock automatically (e.g. relock timer)
588    Auto = 4,
589    /// Lock/unlock operation came from lock button (e.g. one touch or button)
590    Button = 5,
591    /// Lock/unlock operation came from lock due to a schedule
592    Schedule = 6,
593    /// Lock/unlock operation came from remote node
594    Remote = 7,
595    /// Lock/unlock operation came from RFID card
596    Rfid = 8,
597    /// Lock/unlock operation came from biometric source (e.g. face, fingerprint/fingervein)
598    Biometric = 9,
599    /// Lock/unlock operation came from an interaction defined in Aliro, or user change operation was a step-up credential provisioning as defined in Aliro
600    Aliro = 10,
601}
602
603impl OperationSource {
604    /// Convert from u8 value
605    pub fn from_u8(value: u8) -> Option<Self> {
606        match value {
607            0 => Some(OperationSource::Unspecified),
608            1 => Some(OperationSource::Manual),
609            2 => Some(OperationSource::Proprietaryremote),
610            3 => Some(OperationSource::Keypad),
611            4 => Some(OperationSource::Auto),
612            5 => Some(OperationSource::Button),
613            6 => Some(OperationSource::Schedule),
614            7 => Some(OperationSource::Remote),
615            8 => Some(OperationSource::Rfid),
616            9 => Some(OperationSource::Biometric),
617            10 => Some(OperationSource::Aliro),
618            _ => None,
619        }
620    }
621
622    /// Convert to u8 value
623    pub fn to_u8(self) -> u8 {
624        self as u8
625    }
626}
627
628impl From<OperationSource> for u8 {
629    fn from(val: OperationSource) -> Self {
630        val as u8
631    }
632}
633
634#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
635#[repr(u8)]
636pub enum SoundVolume {
637    /// Silent Mode
638    Silent = 0,
639    /// Low Volume
640    Low = 1,
641    /// High Volume
642    High = 2,
643    /// Medium Volume
644    Medium = 3,
645}
646
647impl SoundVolume {
648    /// Convert from u8 value
649    pub fn from_u8(value: u8) -> Option<Self> {
650        match value {
651            0 => Some(SoundVolume::Silent),
652            1 => Some(SoundVolume::Low),
653            2 => Some(SoundVolume::High),
654            3 => Some(SoundVolume::Medium),
655            _ => None,
656        }
657    }
658
659    /// Convert to u8 value
660    pub fn to_u8(self) -> u8 {
661        self as u8
662    }
663}
664
665impl From<SoundVolume> for u8 {
666    fn from(val: SoundVolume) -> Self {
667        val as u8
668    }
669}
670
671#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
672#[repr(u8)]
673pub enum StatusCode {
674    /// Entry would cause a duplicate credential/ID.
675    Duplicate = 2,
676    /// Entry would replace an occupied slot.
677    Occupied = 3,
678}
679
680impl StatusCode {
681    /// Convert from u8 value
682    pub fn from_u8(value: u8) -> Option<Self> {
683        match value {
684            2 => Some(StatusCode::Duplicate),
685            3 => Some(StatusCode::Occupied),
686            _ => None,
687        }
688    }
689
690    /// Convert to u8 value
691    pub fn to_u8(self) -> u8 {
692        self as u8
693    }
694}
695
696impl From<StatusCode> for u8 {
697    fn from(val: StatusCode) -> Self {
698        val as u8
699    }
700}
701
702#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
703#[repr(u8)]
704pub enum UserStatus {
705    /// The user ID is available
706    Available = 0,
707    /// The user ID is occupied and enabled
708    Occupiedenabled = 1,
709    /// The user ID is occupied and disabled
710    Occupieddisabled = 3,
711}
712
713impl UserStatus {
714    /// Convert from u8 value
715    pub fn from_u8(value: u8) -> Option<Self> {
716        match value {
717            0 => Some(UserStatus::Available),
718            1 => Some(UserStatus::Occupiedenabled),
719            3 => Some(UserStatus::Occupieddisabled),
720            _ => None,
721        }
722    }
723
724    /// Convert to u8 value
725    pub fn to_u8(self) -> u8 {
726        self as u8
727    }
728}
729
730impl From<UserStatus> for u8 {
731    fn from(val: UserStatus) -> Self {
732        val as u8
733    }
734}
735
736#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
737#[repr(u8)]
738pub enum UserType {
739    /// The user ID type is unrestricted
740    Unrestricteduser = 0,
741    /// The user ID type is schedule
742    Yeardayscheduleuser = 1,
743    /// The user ID type is schedule
744    Weekdayscheduleuser = 2,
745    /// The user ID type is programming
746    Programminguser = 3,
747    /// The user ID type is non access
748    Nonaccessuser = 4,
749    /// The user ID type is forced
750    Forceduser = 5,
751    /// The user ID type is disposable
752    Disposableuser = 6,
753    /// The user ID type is expiring
754    Expiringuser = 7,
755    /// The user ID type is schedule restricted
756    Schedulerestricteduser = 8,
757    /// The user ID type is remote only
758    Remoteonlyuser = 9,
759}
760
761impl UserType {
762    /// Convert from u8 value
763    pub fn from_u8(value: u8) -> Option<Self> {
764        match value {
765            0 => Some(UserType::Unrestricteduser),
766            1 => Some(UserType::Yeardayscheduleuser),
767            2 => Some(UserType::Weekdayscheduleuser),
768            3 => Some(UserType::Programminguser),
769            4 => Some(UserType::Nonaccessuser),
770            5 => Some(UserType::Forceduser),
771            6 => Some(UserType::Disposableuser),
772            7 => Some(UserType::Expiringuser),
773            8 => Some(UserType::Schedulerestricteduser),
774            9 => Some(UserType::Remoteonlyuser),
775            _ => None,
776        }
777    }
778
779    /// Convert to u8 value
780    pub fn to_u8(self) -> u8 {
781        self as u8
782    }
783}
784
785impl From<UserType> for u8 {
786    fn from(val: UserType) -> Self {
787        val as u8
788    }
789}
790
791// Bitmap definitions
792
793/// AlarmMask bitmap type
794pub type AlarmMask = u8;
795
796/// Constants for AlarmMask
797pub mod alarmmask {
798    /// Locking Mechanism Jammed
799    pub const LOCK_JAMMED: u8 = 0x01;
800    /// Lock Reset to Factory Defaults
801    pub const LOCK_FACTORY_RESET: u8 = 0x02;
802    /// RF Module Power Cycled
803    pub const LOCK_RADIO_POWER_CYCLED: u8 = 0x08;
804    /// Tamper Alarm - wrong code entry limit
805    pub const WRONG_CODE_ENTRY_LIMIT: u8 = 0x10;
806    /// Tamper Alarm - front escutcheon removed from main
807    pub const FRONT_ESCUTCHEON_REMOVED: u8 = 0x20;
808    /// Forced Door Open under Door Locked Condition
809    pub const DOOR_FORCED_OPEN: u8 = 0x40;
810}
811
812/// ConfigurationRegister bitmap type
813pub type ConfigurationRegister = u8;
814
815/// Constants for ConfigurationRegister
816pub mod configurationregister {
817    /// The state of local programming functionality
818    pub const LOCAL_PROGRAMMING: u8 = 0x01;
819    /// The state of the keypad interface
820    pub const KEYPAD_INTERFACE: u8 = 0x02;
821    /// The state of the remote interface
822    pub const REMOTE_INTERFACE: u8 = 0x04;
823    /// Sound volume is set to Silent value
824    pub const SOUND_VOLUME: u8 = 0x20;
825    /// Auto relock time it set to 0
826    pub const AUTO_RELOCK_TIME: u8 = 0x40;
827    /// LEDs is disabled
828    pub const LEDSETTINGS: u8 = 0x80;
829}
830
831/// CredentialRules bitmap type
832pub type CredentialRules = u8;
833
834/// Constants for CredentialRules
835pub mod credentialrules {
836    /// Only one credential is required for lock operation
837    pub const SINGLE: u8 = 0x01;
838    /// Any two credentials are required for lock operation
839    pub const DUAL: u8 = 0x02;
840    /// Any three credentials are required for lock operation
841    pub const TRI: u8 = 0x04;
842}
843
844/// DaysMask bitmap type
845pub type DaysMask = u8;
846
847/// Constants for DaysMask
848pub mod daysmask {
849    /// Schedule is applied on Sunday
850    pub const SUNDAY: u8 = 0x01;
851    /// Schedule is applied on Monday
852    pub const MONDAY: u8 = 0x02;
853    /// Schedule is applied on Tuesday
854    pub const TUESDAY: u8 = 0x04;
855    /// Schedule is applied on Wednesday
856    pub const WEDNESDAY: u8 = 0x08;
857    /// Schedule is applied on Thursday
858    pub const THURSDAY: u8 = 0x10;
859    /// Schedule is applied on Friday
860    pub const FRIDAY: u8 = 0x20;
861    /// Schedule is applied on Saturday
862    pub const SATURDAY: u8 = 0x40;
863}
864
865/// LocalProgrammingFeatures bitmap type
866pub type LocalProgrammingFeatures = u8;
867
868/// Constants for LocalProgrammingFeatures
869pub mod localprogrammingfeatures {
870    /// The state of the ability to add users, credentials or schedules on the device
871    pub const ADD_USERS_CREDENTIALS_SCHEDULES: u8 = 0x01;
872    /// The state of the ability to modify users, credentials or schedules on the device
873    pub const MODIFY_USERS_CREDENTIALS_SCHEDULES: u8 = 0x02;
874    /// The state of the ability to clear users, credentials or schedules on the device
875    pub const CLEAR_USERS_CREDENTIALS_SCHEDULES: u8 = 0x04;
876    /// The state of the ability to adjust settings on the device
877    pub const ADJUST_SETTINGS: u8 = 0x08;
878}
879
880/// OperatingModes bitmap type
881pub type OperatingModes = u8;
882
883/// Constants for OperatingModes
884pub mod operatingmodes {
885    /// Normal operation mode
886    pub const NORMAL: u8 = 0x01;
887    /// Vacation operation mode
888    pub const VACATION: u8 = 0x02;
889    /// Privacy operation mode
890    pub const PRIVACY: u8 = 0x04;
891    /// No remote lock and unlock operation mode
892    pub const NO_REMOTE_LOCK_UNLOCK: u8 = 0x08;
893    /// Passage operation mode
894    pub const PASSAGE: u8 = 0x10;
895}
896
897// Struct definitions
898
899#[derive(Debug, serde::Serialize)]
900pub struct Credential {
901    pub credential_type: Option<CredentialType>,
902    pub credential_index: Option<u16>,
903}
904
905// Command encoders
906
907/// Encode LockDoor command (0x00)
908pub fn encode_lock_door(pin_code: Vec<u8>) -> anyhow::Result<Vec<u8>> {
909    let tlv = tlv::TlvItemEnc {
910        tag: 0,
911        value: tlv::TlvItemValueEnc::StructInvisible(vec![
912        (0, tlv::TlvItemValueEnc::OctetString(pin_code)).into(),
913        ]),
914    };
915    Ok(tlv.encode()?)
916}
917
918/// Encode UnlockDoor command (0x01)
919pub fn encode_unlock_door(pin_code: Vec<u8>) -> anyhow::Result<Vec<u8>> {
920    let tlv = tlv::TlvItemEnc {
921        tag: 0,
922        value: tlv::TlvItemValueEnc::StructInvisible(vec![
923        (0, tlv::TlvItemValueEnc::OctetString(pin_code)).into(),
924        ]),
925    };
926    Ok(tlv.encode()?)
927}
928
929/// Encode UnlockWithTimeout command (0x03)
930pub fn encode_unlock_with_timeout(timeout: u16, pin_code: Vec<u8>) -> anyhow::Result<Vec<u8>> {
931    let tlv = tlv::TlvItemEnc {
932        tag: 0,
933        value: tlv::TlvItemValueEnc::StructInvisible(vec![
934        (0, tlv::TlvItemValueEnc::UInt16(timeout)).into(),
935        (1, tlv::TlvItemValueEnc::OctetString(pin_code)).into(),
936        ]),
937    };
938    Ok(tlv.encode()?)
939}
940
941/// Encode SetWeekDaySchedule command (0x0B)
942pub fn encode_set_week_day_schedule(week_day_index: u8, user_index: u16, days_mask: DaysMask, start_hour: u8, start_minute: u8, end_hour: u8, end_minute: u8) -> anyhow::Result<Vec<u8>> {
943    let tlv = tlv::TlvItemEnc {
944        tag: 0,
945        value: tlv::TlvItemValueEnc::StructInvisible(vec![
946        (0, tlv::TlvItemValueEnc::UInt8(week_day_index)).into(),
947        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
948        (2, tlv::TlvItemValueEnc::UInt8(days_mask)).into(),
949        (3, tlv::TlvItemValueEnc::UInt8(start_hour)).into(),
950        (4, tlv::TlvItemValueEnc::UInt8(start_minute)).into(),
951        (5, tlv::TlvItemValueEnc::UInt8(end_hour)).into(),
952        (6, tlv::TlvItemValueEnc::UInt8(end_minute)).into(),
953        ]),
954    };
955    Ok(tlv.encode()?)
956}
957
958/// Encode GetWeekDaySchedule command (0x0C)
959pub fn encode_get_week_day_schedule(week_day_index: u8, user_index: u16) -> anyhow::Result<Vec<u8>> {
960    let tlv = tlv::TlvItemEnc {
961        tag: 0,
962        value: tlv::TlvItemValueEnc::StructInvisible(vec![
963        (0, tlv::TlvItemValueEnc::UInt8(week_day_index)).into(),
964        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
965        ]),
966    };
967    Ok(tlv.encode()?)
968}
969
970/// Encode ClearWeekDaySchedule command (0x0D)
971pub fn encode_clear_week_day_schedule(week_day_index: u8, user_index: u16) -> anyhow::Result<Vec<u8>> {
972    let tlv = tlv::TlvItemEnc {
973        tag: 0,
974        value: tlv::TlvItemValueEnc::StructInvisible(vec![
975        (0, tlv::TlvItemValueEnc::UInt8(week_day_index)).into(),
976        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
977        ]),
978    };
979    Ok(tlv.encode()?)
980}
981
982/// Encode SetYearDaySchedule command (0x0E)
983pub fn encode_set_year_day_schedule(year_day_index: u8, user_index: u16, local_start_time: u64, local_end_time: u64) -> anyhow::Result<Vec<u8>> {
984    let tlv = tlv::TlvItemEnc {
985        tag: 0,
986        value: tlv::TlvItemValueEnc::StructInvisible(vec![
987        (0, tlv::TlvItemValueEnc::UInt8(year_day_index)).into(),
988        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
989        (2, tlv::TlvItemValueEnc::UInt64(local_start_time)).into(),
990        (3, tlv::TlvItemValueEnc::UInt64(local_end_time)).into(),
991        ]),
992    };
993    Ok(tlv.encode()?)
994}
995
996/// Encode GetYearDaySchedule command (0x0F)
997pub fn encode_get_year_day_schedule(year_day_index: u8, user_index: u16) -> anyhow::Result<Vec<u8>> {
998    let tlv = tlv::TlvItemEnc {
999        tag: 0,
1000        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1001        (0, tlv::TlvItemValueEnc::UInt8(year_day_index)).into(),
1002        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
1003        ]),
1004    };
1005    Ok(tlv.encode()?)
1006}
1007
1008/// Encode ClearYearDaySchedule command (0x10)
1009pub fn encode_clear_year_day_schedule(year_day_index: u8, user_index: u16) -> anyhow::Result<Vec<u8>> {
1010    let tlv = tlv::TlvItemEnc {
1011        tag: 0,
1012        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1013        (0, tlv::TlvItemValueEnc::UInt8(year_day_index)).into(),
1014        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
1015        ]),
1016    };
1017    Ok(tlv.encode()?)
1018}
1019
1020/// Encode SetHolidaySchedule command (0x11)
1021pub fn encode_set_holiday_schedule(holiday_index: u8, local_start_time: u64, local_end_time: u64, operating_mode: OperatingMode) -> anyhow::Result<Vec<u8>> {
1022    let tlv = tlv::TlvItemEnc {
1023        tag: 0,
1024        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1025        (0, tlv::TlvItemValueEnc::UInt8(holiday_index)).into(),
1026        (1, tlv::TlvItemValueEnc::UInt64(local_start_time)).into(),
1027        (2, tlv::TlvItemValueEnc::UInt64(local_end_time)).into(),
1028        (3, tlv::TlvItemValueEnc::UInt8(operating_mode.to_u8())).into(),
1029        ]),
1030    };
1031    Ok(tlv.encode()?)
1032}
1033
1034/// Encode GetHolidaySchedule command (0x12)
1035pub fn encode_get_holiday_schedule(holiday_index: u8) -> anyhow::Result<Vec<u8>> {
1036    let tlv = tlv::TlvItemEnc {
1037        tag: 0,
1038        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1039        (0, tlv::TlvItemValueEnc::UInt8(holiday_index)).into(),
1040        ]),
1041    };
1042    Ok(tlv.encode()?)
1043}
1044
1045/// Encode ClearHolidaySchedule command (0x13)
1046pub fn encode_clear_holiday_schedule(holiday_index: u8) -> anyhow::Result<Vec<u8>> {
1047    let tlv = tlv::TlvItemEnc {
1048        tag: 0,
1049        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1050        (0, tlv::TlvItemValueEnc::UInt8(holiday_index)).into(),
1051        ]),
1052    };
1053    Ok(tlv.encode()?)
1054}
1055
1056/// Encode SetUser command (0x1A)
1057pub fn encode_set_user(operation_type: DataOperationType, user_index: u16, user_name: Option<String>, user_unique_id: Option<u32>, user_status: Option<UserStatus>, user_type: Option<UserType>, credential_rule: Option<CredentialRule>) -> anyhow::Result<Vec<u8>> {
1058    let tlv = tlv::TlvItemEnc {
1059        tag: 0,
1060        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1061        (0, tlv::TlvItemValueEnc::UInt8(operation_type.to_u8())).into(),
1062        (1, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
1063        (2, tlv::TlvItemValueEnc::String(user_name.unwrap_or("".to_string()))).into(),
1064        (3, tlv::TlvItemValueEnc::UInt32(user_unique_id.unwrap_or(0))).into(),
1065        (4, tlv::TlvItemValueEnc::UInt8(user_status.map(|e| e.to_u8()).unwrap_or(0))).into(),
1066        (5, tlv::TlvItemValueEnc::UInt8(user_type.map(|e| e.to_u8()).unwrap_or(0))).into(),
1067        (6, tlv::TlvItemValueEnc::UInt8(credential_rule.map(|e| e.to_u8()).unwrap_or(0))).into(),
1068        ]),
1069    };
1070    Ok(tlv.encode()?)
1071}
1072
1073/// Encode GetUser command (0x1B)
1074pub fn encode_get_user(user_index: u16) -> anyhow::Result<Vec<u8>> {
1075    let tlv = tlv::TlvItemEnc {
1076        tag: 0,
1077        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1078        (0, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
1079        ]),
1080    };
1081    Ok(tlv.encode()?)
1082}
1083
1084/// Encode ClearUser command (0x1D)
1085pub fn encode_clear_user(user_index: u16) -> anyhow::Result<Vec<u8>> {
1086    let tlv = tlv::TlvItemEnc {
1087        tag: 0,
1088        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1089        (0, tlv::TlvItemValueEnc::UInt16(user_index)).into(),
1090        ]),
1091    };
1092    Ok(tlv.encode()?)
1093}
1094
1095/// Encode SetCredential command (0x22)
1096pub fn encode_set_credential(operation_type: DataOperationType, credential: Credential, credential_data: Vec<u8>, user_index: Option<u16>, user_status: Option<UserStatus>, user_type: Option<UserType>) -> anyhow::Result<Vec<u8>> {
1097            // Encode struct CredentialStruct
1098            let mut credential_fields = Vec::new();
1099            if let Some(x) = credential.credential_type { credential_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
1100            if let Some(x) = credential.credential_index { credential_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
1101    let tlv = tlv::TlvItemEnc {
1102        tag: 0,
1103        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1104        (0, tlv::TlvItemValueEnc::UInt8(operation_type.to_u8())).into(),
1105        (1, tlv::TlvItemValueEnc::StructInvisible(credential_fields)).into(),
1106        (2, tlv::TlvItemValueEnc::OctetString(credential_data)).into(),
1107        (3, tlv::TlvItemValueEnc::UInt16(user_index.unwrap_or(0))).into(),
1108        (4, tlv::TlvItemValueEnc::UInt8(user_status.map(|e| e.to_u8()).unwrap_or(0))).into(),
1109        (5, tlv::TlvItemValueEnc::UInt8(user_type.map(|e| e.to_u8()).unwrap_or(0))).into(),
1110        ]),
1111    };
1112    Ok(tlv.encode()?)
1113}
1114
1115/// Encode GetCredentialStatus command (0x24)
1116pub fn encode_get_credential_status(credential: Credential) -> anyhow::Result<Vec<u8>> {
1117            // Encode struct CredentialStruct
1118            let mut credential_fields = Vec::new();
1119            if let Some(x) = credential.credential_type { credential_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
1120            if let Some(x) = credential.credential_index { credential_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
1121    let tlv = tlv::TlvItemEnc {
1122        tag: 0,
1123        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1124        (0, tlv::TlvItemValueEnc::StructInvisible(credential_fields)).into(),
1125        ]),
1126    };
1127    Ok(tlv.encode()?)
1128}
1129
1130/// Encode ClearCredential command (0x26)
1131pub fn encode_clear_credential(credential: Option<Credential>) -> anyhow::Result<Vec<u8>> {
1132            // Encode optional struct CredentialStruct
1133            let credential_enc = if let Some(s) = credential {
1134                let mut fields = Vec::new();
1135                if let Some(x) = s.credential_type { fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
1136                if let Some(x) = s.credential_index { fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
1137                tlv::TlvItemValueEnc::StructInvisible(fields)
1138            } else {
1139                tlv::TlvItemValueEnc::StructInvisible(Vec::new())
1140            };
1141    let tlv = tlv::TlvItemEnc {
1142        tag: 0,
1143        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1144        (0, credential_enc).into(),
1145        ]),
1146    };
1147    Ok(tlv.encode()?)
1148}
1149
1150/// Encode UnboltDoor command (0x27)
1151pub fn encode_unbolt_door(pin_code: Vec<u8>) -> anyhow::Result<Vec<u8>> {
1152    let tlv = tlv::TlvItemEnc {
1153        tag: 0,
1154        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1155        (0, tlv::TlvItemValueEnc::OctetString(pin_code)).into(),
1156        ]),
1157    };
1158    Ok(tlv.encode()?)
1159}
1160
1161/// Encode SetAliroReaderConfig command (0x28)
1162pub fn encode_set_aliro_reader_config(signing_key: Vec<u8>, verification_key: Vec<u8>, group_identifier: Vec<u8>, group_resolving_key: Vec<u8>) -> anyhow::Result<Vec<u8>> {
1163    let tlv = tlv::TlvItemEnc {
1164        tag: 0,
1165        value: tlv::TlvItemValueEnc::StructInvisible(vec![
1166        (0, tlv::TlvItemValueEnc::OctetString(signing_key)).into(),
1167        (1, tlv::TlvItemValueEnc::OctetString(verification_key)).into(),
1168        (2, tlv::TlvItemValueEnc::OctetString(group_identifier)).into(),
1169        (3, tlv::TlvItemValueEnc::OctetString(group_resolving_key)).into(),
1170        ]),
1171    };
1172    Ok(tlv.encode()?)
1173}
1174
1175// Attribute decoders
1176
1177/// Decode LockState attribute (0x0000)
1178pub fn decode_lock_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<LockState>> {
1179    if let tlv::TlvItemValue::Int(v) = inp {
1180        Ok(LockState::from_u8(*v as u8))
1181    } else {
1182        Ok(None)
1183    }
1184}
1185
1186/// Decode LockType attribute (0x0001)
1187pub fn decode_lock_type(inp: &tlv::TlvItemValue) -> anyhow::Result<LockType> {
1188    if let tlv::TlvItemValue::Int(v) = inp {
1189        LockType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1190    } else {
1191        Err(anyhow::anyhow!("Expected Integer"))
1192    }
1193}
1194
1195/// Decode ActuatorEnabled attribute (0x0002)
1196pub fn decode_actuator_enabled(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1197    if let tlv::TlvItemValue::Bool(v) = inp {
1198        Ok(*v)
1199    } else {
1200        Err(anyhow::anyhow!("Expected Bool"))
1201    }
1202}
1203
1204/// Decode DoorState attribute (0x0003)
1205pub fn decode_door_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DoorState>> {
1206    if let tlv::TlvItemValue::Int(v) = inp {
1207        Ok(DoorState::from_u8(*v as u8))
1208    } else {
1209        Ok(None)
1210    }
1211}
1212
1213/// Decode DoorOpenEvents attribute (0x0004)
1214pub fn decode_door_open_events(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
1215    if let tlv::TlvItemValue::Int(v) = inp {
1216        Ok(*v as u32)
1217    } else {
1218        Err(anyhow::anyhow!("Expected UInt32"))
1219    }
1220}
1221
1222/// Decode DoorClosedEvents attribute (0x0005)
1223pub fn decode_door_closed_events(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
1224    if let tlv::TlvItemValue::Int(v) = inp {
1225        Ok(*v as u32)
1226    } else {
1227        Err(anyhow::anyhow!("Expected UInt32"))
1228    }
1229}
1230
1231/// Decode OpenPeriod attribute (0x0006)
1232pub fn decode_open_period(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1233    if let tlv::TlvItemValue::Int(v) = inp {
1234        Ok(*v as u16)
1235    } else {
1236        Err(anyhow::anyhow!("Expected UInt16"))
1237    }
1238}
1239
1240/// Decode NumberOfTotalUsersSupported attribute (0x0011)
1241pub fn decode_number_of_total_users_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1242    if let tlv::TlvItemValue::Int(v) = inp {
1243        Ok(*v as u16)
1244    } else {
1245        Err(anyhow::anyhow!("Expected UInt16"))
1246    }
1247}
1248
1249/// Decode NumberOfPINUsersSupported attribute (0x0012)
1250pub fn decode_number_of_pin_users_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1251    if let tlv::TlvItemValue::Int(v) = inp {
1252        Ok(*v as u16)
1253    } else {
1254        Err(anyhow::anyhow!("Expected UInt16"))
1255    }
1256}
1257
1258/// Decode NumberOfRFIDUsersSupported attribute (0x0013)
1259pub fn decode_number_of_rfid_users_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1260    if let tlv::TlvItemValue::Int(v) = inp {
1261        Ok(*v as u16)
1262    } else {
1263        Err(anyhow::anyhow!("Expected UInt16"))
1264    }
1265}
1266
1267/// Decode NumberOfWeekDaySchedulesSupportedPerUser attribute (0x0014)
1268pub fn decode_number_of_week_day_schedules_supported_per_user(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1269    if let tlv::TlvItemValue::Int(v) = inp {
1270        Ok(*v as u8)
1271    } else {
1272        Err(anyhow::anyhow!("Expected UInt8"))
1273    }
1274}
1275
1276/// Decode NumberOfYearDaySchedulesSupportedPerUser attribute (0x0015)
1277pub fn decode_number_of_year_day_schedules_supported_per_user(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1278    if let tlv::TlvItemValue::Int(v) = inp {
1279        Ok(*v as u8)
1280    } else {
1281        Err(anyhow::anyhow!("Expected UInt8"))
1282    }
1283}
1284
1285/// Decode NumberOfHolidaySchedulesSupported attribute (0x0016)
1286pub fn decode_number_of_holiday_schedules_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1287    if let tlv::TlvItemValue::Int(v) = inp {
1288        Ok(*v as u8)
1289    } else {
1290        Err(anyhow::anyhow!("Expected UInt8"))
1291    }
1292}
1293
1294/// Decode MaxPINCodeLength attribute (0x0017)
1295pub fn decode_max_pin_code_length(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1296    if let tlv::TlvItemValue::Int(v) = inp {
1297        Ok(*v as u8)
1298    } else {
1299        Err(anyhow::anyhow!("Expected UInt8"))
1300    }
1301}
1302
1303/// Decode MinPINCodeLength attribute (0x0018)
1304pub fn decode_min_pin_code_length(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1305    if let tlv::TlvItemValue::Int(v) = inp {
1306        Ok(*v as u8)
1307    } else {
1308        Err(anyhow::anyhow!("Expected UInt8"))
1309    }
1310}
1311
1312/// Decode MaxRFIDCodeLength attribute (0x0019)
1313pub fn decode_max_rfid_code_length(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1314    if let tlv::TlvItemValue::Int(v) = inp {
1315        Ok(*v as u8)
1316    } else {
1317        Err(anyhow::anyhow!("Expected UInt8"))
1318    }
1319}
1320
1321/// Decode MinRFIDCodeLength attribute (0x001A)
1322pub fn decode_min_rfid_code_length(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1323    if let tlv::TlvItemValue::Int(v) = inp {
1324        Ok(*v as u8)
1325    } else {
1326        Err(anyhow::anyhow!("Expected UInt8"))
1327    }
1328}
1329
1330/// Decode CredentialRulesSupport attribute (0x001B)
1331pub fn decode_credential_rules_support(inp: &tlv::TlvItemValue) -> anyhow::Result<CredentialRules> {
1332    if let tlv::TlvItemValue::Int(v) = inp {
1333        Ok(*v as u8)
1334    } else {
1335        Err(anyhow::anyhow!("Expected Integer"))
1336    }
1337}
1338
1339/// Decode NumberOfCredentialsSupportedPerUser attribute (0x001C)
1340pub fn decode_number_of_credentials_supported_per_user(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1341    if let tlv::TlvItemValue::Int(v) = inp {
1342        Ok(*v as u8)
1343    } else {
1344        Err(anyhow::anyhow!("Expected UInt8"))
1345    }
1346}
1347
1348/// Decode Language attribute (0x0021)
1349pub fn decode_language(inp: &tlv::TlvItemValue) -> anyhow::Result<String> {
1350    if let tlv::TlvItemValue::String(v) = inp {
1351        Ok(v.clone())
1352    } else {
1353        Err(anyhow::anyhow!("Expected String"))
1354    }
1355}
1356
1357/// Decode LEDSettings attribute (0x0022)
1358pub fn decode_led_settings(inp: &tlv::TlvItemValue) -> anyhow::Result<LEDSetting> {
1359    if let tlv::TlvItemValue::Int(v) = inp {
1360        LEDSetting::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1361    } else {
1362        Err(anyhow::anyhow!("Expected Integer"))
1363    }
1364}
1365
1366/// Decode AutoRelockTime attribute (0x0023)
1367pub fn decode_auto_relock_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
1368    if let tlv::TlvItemValue::Int(v) = inp {
1369        Ok(*v as u32)
1370    } else {
1371        Err(anyhow::anyhow!("Expected UInt32"))
1372    }
1373}
1374
1375/// Decode SoundVolume attribute (0x0024)
1376pub fn decode_sound_volume(inp: &tlv::TlvItemValue) -> anyhow::Result<SoundVolume> {
1377    if let tlv::TlvItemValue::Int(v) = inp {
1378        SoundVolume::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1379    } else {
1380        Err(anyhow::anyhow!("Expected Integer"))
1381    }
1382}
1383
1384/// Decode OperatingMode attribute (0x0025)
1385pub fn decode_operating_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<OperatingMode> {
1386    if let tlv::TlvItemValue::Int(v) = inp {
1387        OperatingMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1388    } else {
1389        Err(anyhow::anyhow!("Expected Integer"))
1390    }
1391}
1392
1393/// Decode SupportedOperatingModes attribute (0x0026)
1394pub fn decode_supported_operating_modes(inp: &tlv::TlvItemValue) -> anyhow::Result<OperatingModes> {
1395    if let tlv::TlvItemValue::Int(v) = inp {
1396        Ok(*v as u8)
1397    } else {
1398        Err(anyhow::anyhow!("Expected Integer"))
1399    }
1400}
1401
1402/// Decode DefaultConfigurationRegister attribute (0x0027)
1403pub fn decode_default_configuration_register(inp: &tlv::TlvItemValue) -> anyhow::Result<ConfigurationRegister> {
1404    if let tlv::TlvItemValue::Int(v) = inp {
1405        Ok(*v as u8)
1406    } else {
1407        Err(anyhow::anyhow!("Expected Integer"))
1408    }
1409}
1410
1411/// Decode EnableLocalProgramming attribute (0x0028)
1412pub fn decode_enable_local_programming(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1413    if let tlv::TlvItemValue::Bool(v) = inp {
1414        Ok(*v)
1415    } else {
1416        Err(anyhow::anyhow!("Expected Bool"))
1417    }
1418}
1419
1420/// Decode EnableOneTouchLocking attribute (0x0029)
1421pub fn decode_enable_one_touch_locking(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1422    if let tlv::TlvItemValue::Bool(v) = inp {
1423        Ok(*v)
1424    } else {
1425        Err(anyhow::anyhow!("Expected Bool"))
1426    }
1427}
1428
1429/// Decode EnableInsideStatusLED attribute (0x002A)
1430pub fn decode_enable_inside_status_led(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1431    if let tlv::TlvItemValue::Bool(v) = inp {
1432        Ok(*v)
1433    } else {
1434        Err(anyhow::anyhow!("Expected Bool"))
1435    }
1436}
1437
1438/// Decode EnablePrivacyModeButton attribute (0x002B)
1439pub fn decode_enable_privacy_mode_button(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1440    if let tlv::TlvItemValue::Bool(v) = inp {
1441        Ok(*v)
1442    } else {
1443        Err(anyhow::anyhow!("Expected Bool"))
1444    }
1445}
1446
1447/// Decode LocalProgrammingFeatures attribute (0x002C)
1448pub fn decode_local_programming_features(inp: &tlv::TlvItemValue) -> anyhow::Result<LocalProgrammingFeatures> {
1449    if let tlv::TlvItemValue::Int(v) = inp {
1450        Ok(*v as u8)
1451    } else {
1452        Err(anyhow::anyhow!("Expected Integer"))
1453    }
1454}
1455
1456/// Decode WrongCodeEntryLimit attribute (0x0030)
1457pub fn decode_wrong_code_entry_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1458    if let tlv::TlvItemValue::Int(v) = inp {
1459        Ok(*v as u8)
1460    } else {
1461        Err(anyhow::anyhow!("Expected UInt8"))
1462    }
1463}
1464
1465/// Decode UserCodeTemporaryDisableTime attribute (0x0031)
1466pub fn decode_user_code_temporary_disable_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1467    if let tlv::TlvItemValue::Int(v) = inp {
1468        Ok(*v as u8)
1469    } else {
1470        Err(anyhow::anyhow!("Expected UInt8"))
1471    }
1472}
1473
1474/// Decode SendPINOverTheAir attribute (0x0032)
1475pub fn decode_send_pin_over_the_air(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1476    if let tlv::TlvItemValue::Bool(v) = inp {
1477        Ok(*v)
1478    } else {
1479        Err(anyhow::anyhow!("Expected Bool"))
1480    }
1481}
1482
1483/// Decode RequirePINforRemoteOperation attribute (0x0033)
1484pub fn decode_require_pinfor_remote_operation(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
1485    if let tlv::TlvItemValue::Bool(v) = inp {
1486        Ok(*v)
1487    } else {
1488        Err(anyhow::anyhow!("Expected Bool"))
1489    }
1490}
1491
1492/// Decode SecurityLevel attribute (0x0034)
1493pub fn decode_security_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1494    if let tlv::TlvItemValue::Int(v) = inp {
1495        Ok(*v as u8)
1496    } else {
1497        Err(anyhow::anyhow!("Expected UInt8"))
1498    }
1499}
1500
1501/// Decode ExpiringUserTimeout attribute (0x0035)
1502pub fn decode_expiring_user_timeout(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1503    if let tlv::TlvItemValue::Int(v) = inp {
1504        Ok(*v as u16)
1505    } else {
1506        Err(anyhow::anyhow!("Expected UInt16"))
1507    }
1508}
1509
1510/// Decode AliroReaderVerificationKey attribute (0x0080)
1511pub fn decode_aliro_reader_verification_key(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1512    if let tlv::TlvItemValue::OctetString(v) = inp {
1513        Ok(Some(v.clone()))
1514    } else {
1515        Ok(None)
1516    }
1517}
1518
1519/// Decode AliroReaderGroupIdentifier attribute (0x0081)
1520pub fn decode_aliro_reader_group_identifier(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1521    if let tlv::TlvItemValue::OctetString(v) = inp {
1522        Ok(Some(v.clone()))
1523    } else {
1524        Ok(None)
1525    }
1526}
1527
1528/// Decode AliroReaderGroupSubIdentifier attribute (0x0082)
1529pub fn decode_aliro_reader_group_sub_identifier(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<u8>> {
1530    if let tlv::TlvItemValue::OctetString(v) = inp {
1531        Ok(v.clone())
1532    } else {
1533        Err(anyhow::anyhow!("Expected OctetString"))
1534    }
1535}
1536
1537/// Decode AliroExpeditedTransactionSupportedProtocolVersions attribute (0x0083)
1538pub fn decode_aliro_expedited_transaction_supported_protocol_versions(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Vec<u8>>> {
1539    let mut res = Vec::new();
1540    if let tlv::TlvItemValue::List(v) = inp {
1541        for item in v {
1542            if let tlv::TlvItemValue::OctetString(o) = &item.value {
1543                res.push(o.clone());
1544            }
1545        }
1546    }
1547    Ok(res)
1548}
1549
1550/// Decode AliroGroupResolvingKey attribute (0x0084)
1551pub fn decode_aliro_group_resolving_key(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1552    if let tlv::TlvItemValue::OctetString(v) = inp {
1553        Ok(Some(v.clone()))
1554    } else {
1555        Ok(None)
1556    }
1557}
1558
1559/// Decode AliroSupportedBLEUWBProtocolVersions attribute (0x0085)
1560pub fn decode_aliro_supported_bleuwb_protocol_versions(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Vec<u8>>> {
1561    let mut res = Vec::new();
1562    if let tlv::TlvItemValue::List(v) = inp {
1563        for item in v {
1564            if let tlv::TlvItemValue::OctetString(o) = &item.value {
1565                res.push(o.clone());
1566            }
1567        }
1568    }
1569    Ok(res)
1570}
1571
1572/// Decode AliroBLEAdvertisingVersion attribute (0x0086)
1573pub fn decode_aliro_ble_advertising_version(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1574    if let tlv::TlvItemValue::Int(v) = inp {
1575        Ok(*v as u8)
1576    } else {
1577        Err(anyhow::anyhow!("Expected UInt8"))
1578    }
1579}
1580
1581/// Decode NumberOfAliroCredentialIssuerKeysSupported attribute (0x0087)
1582pub fn decode_number_of_aliro_credential_issuer_keys_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1583    if let tlv::TlvItemValue::Int(v) = inp {
1584        Ok(*v as u16)
1585    } else {
1586        Err(anyhow::anyhow!("Expected UInt16"))
1587    }
1588}
1589
1590/// Decode NumberOfAliroEndpointKeysSupported attribute (0x0088)
1591pub fn decode_number_of_aliro_endpoint_keys_supported(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1592    if let tlv::TlvItemValue::Int(v) = inp {
1593        Ok(*v as u16)
1594    } else {
1595        Err(anyhow::anyhow!("Expected UInt16"))
1596    }
1597}
1598
1599
1600// JSON dispatcher function
1601
1602/// Decode attribute value and return as JSON string
1603///
1604/// # Parameters
1605/// * `cluster_id` - The cluster identifier
1606/// * `attribute_id` - The attribute identifier
1607/// * `tlv_value` - The TLV value to decode
1608///
1609/// # Returns
1610/// JSON string representation of the decoded value or error
1611pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1612    // Verify this is the correct cluster
1613    if cluster_id != 0x0101 {
1614        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0101, got {}\"}}", cluster_id);
1615    }
1616
1617    match attribute_id {
1618        0x0000 => {
1619            match decode_lock_state(tlv_value) {
1620                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1621                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1622            }
1623        }
1624        0x0001 => {
1625            match decode_lock_type(tlv_value) {
1626                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1627                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1628            }
1629        }
1630        0x0002 => {
1631            match decode_actuator_enabled(tlv_value) {
1632                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1633                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1634            }
1635        }
1636        0x0003 => {
1637            match decode_door_state(tlv_value) {
1638                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1639                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1640            }
1641        }
1642        0x0004 => {
1643            match decode_door_open_events(tlv_value) {
1644                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1645                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1646            }
1647        }
1648        0x0005 => {
1649            match decode_door_closed_events(tlv_value) {
1650                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1651                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1652            }
1653        }
1654        0x0006 => {
1655            match decode_open_period(tlv_value) {
1656                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1657                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1658            }
1659        }
1660        0x0011 => {
1661            match decode_number_of_total_users_supported(tlv_value) {
1662                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1663                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1664            }
1665        }
1666        0x0012 => {
1667            match decode_number_of_pin_users_supported(tlv_value) {
1668                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1669                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1670            }
1671        }
1672        0x0013 => {
1673            match decode_number_of_rfid_users_supported(tlv_value) {
1674                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1675                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1676            }
1677        }
1678        0x0014 => {
1679            match decode_number_of_week_day_schedules_supported_per_user(tlv_value) {
1680                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1681                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1682            }
1683        }
1684        0x0015 => {
1685            match decode_number_of_year_day_schedules_supported_per_user(tlv_value) {
1686                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1687                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1688            }
1689        }
1690        0x0016 => {
1691            match decode_number_of_holiday_schedules_supported(tlv_value) {
1692                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1693                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1694            }
1695        }
1696        0x0017 => {
1697            match decode_max_pin_code_length(tlv_value) {
1698                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1699                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1700            }
1701        }
1702        0x0018 => {
1703            match decode_min_pin_code_length(tlv_value) {
1704                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1705                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1706            }
1707        }
1708        0x0019 => {
1709            match decode_max_rfid_code_length(tlv_value) {
1710                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1711                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1712            }
1713        }
1714        0x001A => {
1715            match decode_min_rfid_code_length(tlv_value) {
1716                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1717                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1718            }
1719        }
1720        0x001B => {
1721            match decode_credential_rules_support(tlv_value) {
1722                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1723                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1724            }
1725        }
1726        0x001C => {
1727            match decode_number_of_credentials_supported_per_user(tlv_value) {
1728                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1729                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1730            }
1731        }
1732        0x0021 => {
1733            match decode_language(tlv_value) {
1734                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1735                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1736            }
1737        }
1738        0x0022 => {
1739            match decode_led_settings(tlv_value) {
1740                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1741                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1742            }
1743        }
1744        0x0023 => {
1745            match decode_auto_relock_time(tlv_value) {
1746                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1747                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1748            }
1749        }
1750        0x0024 => {
1751            match decode_sound_volume(tlv_value) {
1752                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1753                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1754            }
1755        }
1756        0x0025 => {
1757            match decode_operating_mode(tlv_value) {
1758                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1759                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1760            }
1761        }
1762        0x0026 => {
1763            match decode_supported_operating_modes(tlv_value) {
1764                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1765                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1766            }
1767        }
1768        0x0027 => {
1769            match decode_default_configuration_register(tlv_value) {
1770                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1771                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1772            }
1773        }
1774        0x0028 => {
1775            match decode_enable_local_programming(tlv_value) {
1776                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1777                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1778            }
1779        }
1780        0x0029 => {
1781            match decode_enable_one_touch_locking(tlv_value) {
1782                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1783                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1784            }
1785        }
1786        0x002A => {
1787            match decode_enable_inside_status_led(tlv_value) {
1788                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1789                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1790            }
1791        }
1792        0x002B => {
1793            match decode_enable_privacy_mode_button(tlv_value) {
1794                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1795                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1796            }
1797        }
1798        0x002C => {
1799            match decode_local_programming_features(tlv_value) {
1800                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1801                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1802            }
1803        }
1804        0x0030 => {
1805            match decode_wrong_code_entry_limit(tlv_value) {
1806                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1807                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1808            }
1809        }
1810        0x0031 => {
1811            match decode_user_code_temporary_disable_time(tlv_value) {
1812                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1813                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1814            }
1815        }
1816        0x0032 => {
1817            match decode_send_pin_over_the_air(tlv_value) {
1818                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1819                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1820            }
1821        }
1822        0x0033 => {
1823            match decode_require_pinfor_remote_operation(tlv_value) {
1824                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1825                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1826            }
1827        }
1828        0x0034 => {
1829            match decode_security_level(tlv_value) {
1830                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1831                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1832            }
1833        }
1834        0x0035 => {
1835            match decode_expiring_user_timeout(tlv_value) {
1836                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1837                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1838            }
1839        }
1840        0x0080 => {
1841            match decode_aliro_reader_verification_key(tlv_value) {
1842                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1843                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1844            }
1845        }
1846        0x0081 => {
1847            match decode_aliro_reader_group_identifier(tlv_value) {
1848                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1849                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1850            }
1851        }
1852        0x0082 => {
1853            match decode_aliro_reader_group_sub_identifier(tlv_value) {
1854                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1855                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1856            }
1857        }
1858        0x0083 => {
1859            match decode_aliro_expedited_transaction_supported_protocol_versions(tlv_value) {
1860                Ok(value) => {
1861                    // Serialize Vec<Vec<u8>> as array of hex strings
1862                    let hex_array: Vec<String> = value.iter()
1863                        .map(|bytes| bytes.iter()
1864                            .map(|byte| format!("{:02x}", byte))
1865                            .collect::<String>())
1866                        .collect();
1867                    serde_json::to_string(&hex_array).unwrap_or_else(|_| "null".to_string())
1868                },
1869                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1870            }
1871        }
1872        0x0084 => {
1873            match decode_aliro_group_resolving_key(tlv_value) {
1874                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1875                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1876            }
1877        }
1878        0x0085 => {
1879            match decode_aliro_supported_bleuwb_protocol_versions(tlv_value) {
1880                Ok(value) => {
1881                    // Serialize Vec<Vec<u8>> as array of hex strings
1882                    let hex_array: Vec<String> = value.iter()
1883                        .map(|bytes| bytes.iter()
1884                            .map(|byte| format!("{:02x}", byte))
1885                            .collect::<String>())
1886                        .collect();
1887                    serde_json::to_string(&hex_array).unwrap_or_else(|_| "null".to_string())
1888                },
1889                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1890            }
1891        }
1892        0x0086 => {
1893            match decode_aliro_ble_advertising_version(tlv_value) {
1894                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1895                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1896            }
1897        }
1898        0x0087 => {
1899            match decode_number_of_aliro_credential_issuer_keys_supported(tlv_value) {
1900                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1901                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1902            }
1903        }
1904        0x0088 => {
1905            match decode_number_of_aliro_endpoint_keys_supported(tlv_value) {
1906                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1907                Err(e) => format!("{{\"error\": \"{}\"}}", e),
1908            }
1909        }
1910        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
1911    }
1912}
1913
1914/// Get list of all attributes supported by this cluster
1915///
1916/// # Returns
1917/// Vector of tuples containing (attribute_id, attribute_name)
1918pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
1919    vec![
1920        (0x0000, "LockState"),
1921        (0x0001, "LockType"),
1922        (0x0002, "ActuatorEnabled"),
1923        (0x0003, "DoorState"),
1924        (0x0004, "DoorOpenEvents"),
1925        (0x0005, "DoorClosedEvents"),
1926        (0x0006, "OpenPeriod"),
1927        (0x0011, "NumberOfTotalUsersSupported"),
1928        (0x0012, "NumberOfPINUsersSupported"),
1929        (0x0013, "NumberOfRFIDUsersSupported"),
1930        (0x0014, "NumberOfWeekDaySchedulesSupportedPerUser"),
1931        (0x0015, "NumberOfYearDaySchedulesSupportedPerUser"),
1932        (0x0016, "NumberOfHolidaySchedulesSupported"),
1933        (0x0017, "MaxPINCodeLength"),
1934        (0x0018, "MinPINCodeLength"),
1935        (0x0019, "MaxRFIDCodeLength"),
1936        (0x001A, "MinRFIDCodeLength"),
1937        (0x001B, "CredentialRulesSupport"),
1938        (0x001C, "NumberOfCredentialsSupportedPerUser"),
1939        (0x0021, "Language"),
1940        (0x0022, "LEDSettings"),
1941        (0x0023, "AutoRelockTime"),
1942        (0x0024, "SoundVolume"),
1943        (0x0025, "OperatingMode"),
1944        (0x0026, "SupportedOperatingModes"),
1945        (0x0027, "DefaultConfigurationRegister"),
1946        (0x0028, "EnableLocalProgramming"),
1947        (0x0029, "EnableOneTouchLocking"),
1948        (0x002A, "EnableInsideStatusLED"),
1949        (0x002B, "EnablePrivacyModeButton"),
1950        (0x002C, "LocalProgrammingFeatures"),
1951        (0x0030, "WrongCodeEntryLimit"),
1952        (0x0031, "UserCodeTemporaryDisableTime"),
1953        (0x0032, "SendPINOverTheAir"),
1954        (0x0033, "RequirePINforRemoteOperation"),
1955        (0x0034, "SecurityLevel"),
1956        (0x0035, "ExpiringUserTimeout"),
1957        (0x0080, "AliroReaderVerificationKey"),
1958        (0x0081, "AliroReaderGroupIdentifier"),
1959        (0x0082, "AliroReaderGroupSubIdentifier"),
1960        (0x0083, "AliroExpeditedTransactionSupportedProtocolVersions"),
1961        (0x0084, "AliroGroupResolvingKey"),
1962        (0x0085, "AliroSupportedBLEUWBProtocolVersions"),
1963        (0x0086, "AliroBLEAdvertisingVersion"),
1964        (0x0087, "NumberOfAliroCredentialIssuerKeysSupported"),
1965        (0x0088, "NumberOfAliroEndpointKeysSupported"),
1966    ]
1967}
1968
1969#[derive(Debug, serde::Serialize)]
1970pub struct GetWeekDayScheduleResponse {
1971    pub week_day_index: Option<u8>,
1972    pub user_index: Option<u16>,
1973    pub status: Option<u8>,
1974    pub days_mask: Option<DaysMask>,
1975    pub start_hour: Option<u8>,
1976    pub start_minute: Option<u8>,
1977    pub end_hour: Option<u8>,
1978    pub end_minute: Option<u8>,
1979}
1980
1981#[derive(Debug, serde::Serialize)]
1982pub struct GetYearDayScheduleResponse {
1983    pub year_day_index: Option<u8>,
1984    pub user_index: Option<u16>,
1985    pub status: Option<u8>,
1986    pub local_start_time: Option<u64>,
1987    pub local_end_time: Option<u64>,
1988}
1989
1990#[derive(Debug, serde::Serialize)]
1991pub struct GetHolidayScheduleResponse {
1992    pub holiday_index: Option<u8>,
1993    pub status: Option<u8>,
1994    pub local_start_time: Option<u64>,
1995    pub local_end_time: Option<u64>,
1996    pub operating_mode: Option<OperatingMode>,
1997}
1998
1999#[derive(Debug, serde::Serialize)]
2000pub struct GetUserResponse {
2001    pub user_index: Option<u16>,
2002    pub user_name: Option<String>,
2003    pub user_unique_id: Option<u32>,
2004    pub user_status: Option<UserStatus>,
2005    pub user_type: Option<UserType>,
2006    pub credential_rule: Option<CredentialRule>,
2007    pub credentials: Option<Vec<Credential>>,
2008    pub creator_fabric_index: Option<u8>,
2009    pub last_modified_fabric_index: Option<u8>,
2010    pub next_user_index: Option<u16>,
2011}
2012
2013#[derive(Debug, serde::Serialize)]
2014pub struct SetCredentialResponse {
2015    pub status: Option<u8>,
2016    pub user_index: Option<u16>,
2017    pub next_credential_index: Option<u16>,
2018}
2019
2020#[derive(Debug, serde::Serialize)]
2021pub struct GetCredentialStatusResponse {
2022    pub credential_exists: Option<bool>,
2023    pub user_index: Option<u16>,
2024    pub creator_fabric_index: Option<u8>,
2025    pub last_modified_fabric_index: Option<u8>,
2026    pub next_credential_index: Option<u16>,
2027    #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
2028    pub credential_data: Option<Vec<u8>>,
2029}
2030
2031// Command response decoders
2032
2033/// Decode GetWeekDayScheduleResponse command response (0C)
2034pub fn decode_get_week_day_schedule_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetWeekDayScheduleResponse> {
2035    if let tlv::TlvItemValue::List(_fields) = inp {
2036        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2037        Ok(GetWeekDayScheduleResponse {
2038                week_day_index: item.get_int(&[0]).map(|v| v as u8),
2039                user_index: item.get_int(&[1]).map(|v| v as u16),
2040                status: item.get_int(&[2]).map(|v| v as u8),
2041                days_mask: item.get_int(&[3]).map(|v| v as u8),
2042                start_hour: item.get_int(&[4]).map(|v| v as u8),
2043                start_minute: item.get_int(&[5]).map(|v| v as u8),
2044                end_hour: item.get_int(&[6]).map(|v| v as u8),
2045                end_minute: item.get_int(&[7]).map(|v| v as u8),
2046        })
2047    } else {
2048        Err(anyhow::anyhow!("Expected struct fields"))
2049    }
2050}
2051
2052/// Decode GetYearDayScheduleResponse command response (0F)
2053pub fn decode_get_year_day_schedule_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetYearDayScheduleResponse> {
2054    if let tlv::TlvItemValue::List(_fields) = inp {
2055        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2056        Ok(GetYearDayScheduleResponse {
2057                year_day_index: item.get_int(&[0]).map(|v| v as u8),
2058                user_index: item.get_int(&[1]).map(|v| v as u16),
2059                status: item.get_int(&[2]).map(|v| v as u8),
2060                local_start_time: item.get_int(&[3]),
2061                local_end_time: item.get_int(&[4]),
2062        })
2063    } else {
2064        Err(anyhow::anyhow!("Expected struct fields"))
2065    }
2066}
2067
2068/// Decode GetHolidayScheduleResponse command response (12)
2069pub fn decode_get_holiday_schedule_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetHolidayScheduleResponse> {
2070    if let tlv::TlvItemValue::List(_fields) = inp {
2071        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2072        Ok(GetHolidayScheduleResponse {
2073                holiday_index: item.get_int(&[0]).map(|v| v as u8),
2074                status: item.get_int(&[1]).map(|v| v as u8),
2075                local_start_time: item.get_int(&[2]),
2076                local_end_time: item.get_int(&[3]),
2077                operating_mode: item.get_int(&[4]).and_then(|v| OperatingMode::from_u8(v as u8)),
2078        })
2079    } else {
2080        Err(anyhow::anyhow!("Expected struct fields"))
2081    }
2082}
2083
2084/// Decode GetUserResponse command response (1C)
2085pub fn decode_get_user_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetUserResponse> {
2086    if let tlv::TlvItemValue::List(_fields) = inp {
2087        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2088        Ok(GetUserResponse {
2089                user_index: item.get_int(&[0]).map(|v| v as u16),
2090                user_name: item.get_string_owned(&[1]),
2091                user_unique_id: item.get_int(&[2]).map(|v| v as u32),
2092                user_status: item.get_int(&[3]).and_then(|v| UserStatus::from_u8(v as u8)),
2093                user_type: item.get_int(&[4]).and_then(|v| UserType::from_u8(v as u8)),
2094                credential_rule: item.get_int(&[5]).and_then(|v| CredentialRule::from_u8(v as u8)),
2095                credentials: {
2096                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[6]) {
2097                        let mut items = Vec::new();
2098                        for list_item in l {
2099                            items.push(Credential {
2100                credential_type: list_item.get_int(&[0]).and_then(|v| CredentialType::from_u8(v as u8)),
2101                credential_index: list_item.get_int(&[1]).map(|v| v as u16),
2102                            });
2103                        }
2104                        Some(items)
2105                    } else {
2106                        None
2107                    }
2108                },
2109                creator_fabric_index: item.get_int(&[7]).map(|v| v as u8),
2110                last_modified_fabric_index: item.get_int(&[8]).map(|v| v as u8),
2111                next_user_index: item.get_int(&[9]).map(|v| v as u16),
2112        })
2113    } else {
2114        Err(anyhow::anyhow!("Expected struct fields"))
2115    }
2116}
2117
2118/// Decode SetCredentialResponse command response (23)
2119pub fn decode_set_credential_response(inp: &tlv::TlvItemValue) -> anyhow::Result<SetCredentialResponse> {
2120    if let tlv::TlvItemValue::List(_fields) = inp {
2121        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2122        Ok(SetCredentialResponse {
2123                status: item.get_int(&[0]).map(|v| v as u8),
2124                user_index: item.get_int(&[1]).map(|v| v as u16),
2125                next_credential_index: item.get_int(&[2]).map(|v| v as u16),
2126        })
2127    } else {
2128        Err(anyhow::anyhow!("Expected struct fields"))
2129    }
2130}
2131
2132/// Decode GetCredentialStatusResponse command response (25)
2133pub fn decode_get_credential_status_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetCredentialStatusResponse> {
2134    if let tlv::TlvItemValue::List(_fields) = inp {
2135        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2136        Ok(GetCredentialStatusResponse {
2137                credential_exists: item.get_bool(&[0]),
2138                user_index: item.get_int(&[1]).map(|v| v as u16),
2139                creator_fabric_index: item.get_int(&[2]).map(|v| v as u8),
2140                last_modified_fabric_index: item.get_int(&[3]).map(|v| v as u8),
2141                next_credential_index: item.get_int(&[4]).map(|v| v as u16),
2142                credential_data: item.get_octet_string_owned(&[5]),
2143        })
2144    } else {
2145        Err(anyhow::anyhow!("Expected struct fields"))
2146    }
2147}
2148
2149#[derive(Debug, serde::Serialize)]
2150pub struct DoorLockAlarmEvent {
2151    pub alarm_code: Option<AlarmCode>,
2152}
2153
2154#[derive(Debug, serde::Serialize)]
2155pub struct DoorStateChangeEvent {
2156    pub door_state: Option<DoorState>,
2157}
2158
2159#[derive(Debug, serde::Serialize)]
2160pub struct LockOperationEvent {
2161    pub lock_operation_type: Option<LockOperationType>,
2162    pub operation_source: Option<OperationSource>,
2163    pub user_index: Option<u16>,
2164    pub fabric_index: Option<u8>,
2165    pub source_node: Option<u64>,
2166    pub credentials: Option<Vec<Credential>>,
2167}
2168
2169#[derive(Debug, serde::Serialize)]
2170pub struct LockOperationErrorEvent {
2171    pub lock_operation_type: Option<LockOperationType>,
2172    pub operation_source: Option<OperationSource>,
2173    pub operation_error: Option<OperationError>,
2174    pub user_index: Option<u16>,
2175    pub fabric_index: Option<u8>,
2176    pub source_node: Option<u64>,
2177    pub credentials: Option<Vec<Credential>>,
2178}
2179
2180#[derive(Debug, serde::Serialize)]
2181pub struct LockUserChangeEvent {
2182    pub lock_data_type: Option<LockDataType>,
2183    pub data_operation_type: Option<DataOperationType>,
2184    pub operation_source: Option<OperationSource>,
2185    pub user_index: Option<u16>,
2186    pub fabric_index: Option<u8>,
2187    pub source_node: Option<u64>,
2188    pub data_index: Option<u16>,
2189}
2190
2191// Event decoders
2192
2193/// Decode DoorLockAlarm event (0x00, priority: critical)
2194pub fn decode_door_lock_alarm_event(inp: &tlv::TlvItemValue) -> anyhow::Result<DoorLockAlarmEvent> {
2195    if let tlv::TlvItemValue::List(_fields) = inp {
2196        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2197        Ok(DoorLockAlarmEvent {
2198                                alarm_code: item.get_int(&[0]).and_then(|v| AlarmCode::from_u8(v as u8)),
2199        })
2200    } else {
2201        Err(anyhow::anyhow!("Expected struct fields"))
2202    }
2203}
2204
2205/// Decode DoorStateChange event (0x01, priority: desc)
2206pub fn decode_door_state_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<DoorStateChangeEvent> {
2207    if let tlv::TlvItemValue::List(_fields) = inp {
2208        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2209        Ok(DoorStateChangeEvent {
2210                                door_state: item.get_int(&[0]).and_then(|v| DoorState::from_u8(v as u8)),
2211        })
2212    } else {
2213        Err(anyhow::anyhow!("Expected struct fields"))
2214    }
2215}
2216
2217/// Decode LockOperation event (0x02, priority: desc)
2218pub fn decode_lock_operation_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LockOperationEvent> {
2219    if let tlv::TlvItemValue::List(_fields) = inp {
2220        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2221        Ok(LockOperationEvent {
2222                                lock_operation_type: item.get_int(&[0]).and_then(|v| LockOperationType::from_u8(v as u8)),
2223                                operation_source: item.get_int(&[1]).and_then(|v| OperationSource::from_u8(v as u8)),
2224                                user_index: item.get_int(&[2]).map(|v| v as u16),
2225                                fabric_index: item.get_int(&[3]).map(|v| v as u8),
2226                                source_node: item.get_int(&[4]),
2227                                credentials: {
2228                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[5]) {
2229                        let mut items = Vec::new();
2230                        for list_item in l {
2231                            items.push(Credential {
2232                credential_type: list_item.get_int(&[0]).and_then(|v| CredentialType::from_u8(v as u8)),
2233                credential_index: list_item.get_int(&[1]).map(|v| v as u16),
2234                            });
2235                        }
2236                        Some(items)
2237                    } else {
2238                        None
2239                    }
2240                },
2241        })
2242    } else {
2243        Err(anyhow::anyhow!("Expected struct fields"))
2244    }
2245}
2246
2247/// Decode LockOperationError event (0x03, priority: desc)
2248pub fn decode_lock_operation_error_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LockOperationErrorEvent> {
2249    if let tlv::TlvItemValue::List(_fields) = inp {
2250        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2251        Ok(LockOperationErrorEvent {
2252                                lock_operation_type: item.get_int(&[0]).and_then(|v| LockOperationType::from_u8(v as u8)),
2253                                operation_source: item.get_int(&[1]).and_then(|v| OperationSource::from_u8(v as u8)),
2254                                operation_error: item.get_int(&[2]).and_then(|v| OperationError::from_u8(v as u8)),
2255                                user_index: item.get_int(&[3]).map(|v| v as u16),
2256                                fabric_index: item.get_int(&[4]).map(|v| v as u8),
2257                                source_node: item.get_int(&[5]),
2258                                credentials: {
2259                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[6]) {
2260                        let mut items = Vec::new();
2261                        for list_item in l {
2262                            items.push(Credential {
2263                credential_type: list_item.get_int(&[0]).and_then(|v| CredentialType::from_u8(v as u8)),
2264                credential_index: list_item.get_int(&[1]).map(|v| v as u16),
2265                            });
2266                        }
2267                        Some(items)
2268                    } else {
2269                        None
2270                    }
2271                },
2272        })
2273    } else {
2274        Err(anyhow::anyhow!("Expected struct fields"))
2275    }
2276}
2277
2278/// Decode LockUserChange event (0x04, priority: info)
2279pub fn decode_lock_user_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LockUserChangeEvent> {
2280    if let tlv::TlvItemValue::List(_fields) = inp {
2281        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2282        Ok(LockUserChangeEvent {
2283                                lock_data_type: item.get_int(&[0]).and_then(|v| LockDataType::from_u8(v as u8)),
2284                                data_operation_type: item.get_int(&[1]).and_then(|v| DataOperationType::from_u8(v as u8)),
2285                                operation_source: item.get_int(&[2]).and_then(|v| OperationSource::from_u8(v as u8)),
2286                                user_index: item.get_int(&[3]).map(|v| v as u16),
2287                                fabric_index: item.get_int(&[4]).map(|v| v as u8),
2288                                source_node: item.get_int(&[5]),
2289                                data_index: item.get_int(&[6]).map(|v| v as u16),
2290        })
2291    } else {
2292        Err(anyhow::anyhow!("Expected struct fields"))
2293    }
2294}
2295