1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum AccessControlAuxiliaryType {
21 System = 0,
23 Groupcast = 1,
25}
26
27impl AccessControlAuxiliaryType {
28 pub fn from_u8(value: u8) -> Option<Self> {
30 match value {
31 0 => Some(AccessControlAuxiliaryType::System),
32 1 => Some(AccessControlAuxiliaryType::Groupcast),
33 _ => None,
34 }
35 }
36
37 pub fn to_u8(self) -> u8 {
39 self as u8
40 }
41}
42
43impl From<AccessControlAuxiliaryType> for u8 {
44 fn from(val: AccessControlAuxiliaryType) -> Self {
45 val as u8
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum AccessControlEntryAuthMode {
52 Pase = 1,
54 Case = 2,
56 Group = 3,
58}
59
60impl AccessControlEntryAuthMode {
61 pub fn from_u8(value: u8) -> Option<Self> {
63 match value {
64 1 => Some(AccessControlEntryAuthMode::Pase),
65 2 => Some(AccessControlEntryAuthMode::Case),
66 3 => Some(AccessControlEntryAuthMode::Group),
67 _ => None,
68 }
69 }
70
71 pub fn to_u8(self) -> u8 {
73 self as u8
74 }
75}
76
77impl From<AccessControlEntryAuthMode> for u8 {
78 fn from(val: AccessControlEntryAuthMode) -> Self {
79 val as u8
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[repr(u8)]
85pub enum AccessControlEntryPrivilege {
86 View = 1,
88 Proxyview = 2,
89 Operate = 3,
91 Manage = 4,
93 Administer = 5,
95}
96
97impl AccessControlEntryPrivilege {
98 pub fn from_u8(value: u8) -> Option<Self> {
100 match value {
101 1 => Some(AccessControlEntryPrivilege::View),
102 2 => Some(AccessControlEntryPrivilege::Proxyview),
103 3 => Some(AccessControlEntryPrivilege::Operate),
104 4 => Some(AccessControlEntryPrivilege::Manage),
105 5 => Some(AccessControlEntryPrivilege::Administer),
106 _ => None,
107 }
108 }
109
110 pub fn to_u8(self) -> u8 {
112 self as u8
113 }
114}
115
116impl From<AccessControlEntryPrivilege> for u8 {
117 fn from(val: AccessControlEntryPrivilege) -> Self {
118 val as u8
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
123#[repr(u8)]
124pub enum AccessRestrictionType {
125 Attributeaccessforbidden = 0,
127 Attributewriteforbidden = 1,
129 Commandforbidden = 2,
131 Eventforbidden = 3,
133}
134
135impl AccessRestrictionType {
136 pub fn from_u8(value: u8) -> Option<Self> {
138 match value {
139 0 => Some(AccessRestrictionType::Attributeaccessforbidden),
140 1 => Some(AccessRestrictionType::Attributewriteforbidden),
141 2 => Some(AccessRestrictionType::Commandforbidden),
142 3 => Some(AccessRestrictionType::Eventforbidden),
143 _ => None,
144 }
145 }
146
147 pub fn to_u8(self) -> u8 {
149 self as u8
150 }
151}
152
153impl From<AccessRestrictionType> for u8 {
154 fn from(val: AccessRestrictionType) -> Self {
155 val as u8
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
160#[repr(u8)]
161pub enum ChangeType {
162 Changed = 0,
164 Added = 1,
166 Removed = 2,
168}
169
170impl ChangeType {
171 pub fn from_u8(value: u8) -> Option<Self> {
173 match value {
174 0 => Some(ChangeType::Changed),
175 1 => Some(ChangeType::Added),
176 2 => Some(ChangeType::Removed),
177 _ => None,
178 }
179 }
180
181 pub fn to_u8(self) -> u8 {
183 self as u8
184 }
185}
186
187impl From<ChangeType> for u8 {
188 fn from(val: ChangeType) -> Self {
189 val as u8
190 }
191}
192
193#[derive(Debug, serde::Serialize)]
196pub struct AccessControlEntry {
197 pub privilege: Option<AccessControlEntryPrivilege>,
198 pub auth_mode: Option<AccessControlEntryAuthMode>,
199 pub subjects: Option<Vec<u64>>,
200 pub targets: Option<Vec<AccessControlTarget>>,
201 pub auxiliary_type: Option<AccessControlAuxiliaryType>,
202}
203
204#[derive(Debug, serde::Serialize)]
205pub struct AccessControlExtension {
206 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
207 pub data: Option<Vec<u8>>,
208}
209
210#[derive(Debug, serde::Serialize)]
211pub struct AccessControlTarget {
212 pub cluster: Option<u32>,
213 pub endpoint: Option<u16>,
214 pub device_type: Option<u32>,
215}
216
217#[derive(Debug, serde::Serialize)]
218pub struct AccessRestrictionEntry {
219 pub endpoint: Option<u16>,
220 pub cluster: Option<u32>,
221 pub restrictions: Option<Vec<AccessRestriction>>,
222}
223
224#[derive(Debug, serde::Serialize)]
225pub struct AccessRestriction {
226 pub type_: Option<AccessRestrictionType>,
227 pub id: Option<u32>,
228}
229
230#[derive(Debug, serde::Serialize)]
231pub struct CommissioningAccessRestrictionEntry {
232 pub endpoint: Option<u16>,
233 pub cluster: Option<u32>,
234 pub restrictions: Option<Vec<AccessRestriction>>,
235}
236
237pub fn encode_review_fabric_restrictions(arl: Vec<CommissioningAccessRestrictionEntry>) -> anyhow::Result<Vec<u8>> {
241 let tlv = tlv::TlvItemEnc {
242 tag: 0,
243 value: tlv::TlvItemValueEnc::StructInvisible(vec![
244 (0, tlv::TlvItemValueEnc::Array(arl.into_iter().map(|v| {
245 let mut fields = Vec::new();
246 if let Some(x) = v.endpoint { fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
247 if let Some(x) = v.cluster { fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
248 if let Some(listv) = v.restrictions {
249 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
250 let mut nested_fields = Vec::new();
251 if let Some(x) = inner.type_ { nested_fields.push((0, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
252 if let Some(x) = inner.id { nested_fields.push((1, tlv::TlvItemValueEnc::UInt32(x)).into()); }
253 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
254 }).collect();
255 fields.push((2, tlv::TlvItemValueEnc::Array(inner_vec)).into());
256 }
257 (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
258 }).collect())).into(),
259 ]),
260 };
261 Ok(tlv.encode()?)
262}
263
264pub fn decode_acl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlEntry>> {
268 let mut res = Vec::new();
269 if let tlv::TlvItemValue::List(v) = inp {
270 for item in v {
271 res.push(AccessControlEntry {
272 privilege: item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
273 auth_mode: item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
274 subjects: {
275 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
276 let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
277 Some(items)
278 } else {
279 None
280 }
281 },
282 targets: {
283 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
284 let mut items = Vec::new();
285 for list_item in l {
286 items.push(AccessControlTarget {
287 cluster: list_item.get_int(&[0]).map(|v| v as u32),
288 endpoint: list_item.get_int(&[1]).map(|v| v as u16),
289 device_type: list_item.get_int(&[2]).map(|v| v as u32),
290 });
291 }
292 Some(items)
293 } else {
294 None
295 }
296 },
297 auxiliary_type: item.get_int(&[5]).and_then(|v| AccessControlAuxiliaryType::from_u8(v as u8)),
298 });
299 }
300 }
301 Ok(res)
302}
303
304pub fn decode_extension(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlExtension>> {
306 let mut res = Vec::new();
307 if let tlv::TlvItemValue::List(v) = inp {
308 for item in v {
309 res.push(AccessControlExtension {
310 data: item.get_octet_string_owned(&[1]),
311 });
312 }
313 }
314 Ok(res)
315}
316
317pub fn decode_subjects_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
319 if let tlv::TlvItemValue::Int(v) = inp {
320 Ok(*v as u16)
321 } else {
322 Err(anyhow::anyhow!("Expected UInt16"))
323 }
324}
325
326pub fn decode_targets_per_access_control_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
328 if let tlv::TlvItemValue::Int(v) = inp {
329 Ok(*v as u16)
330 } else {
331 Err(anyhow::anyhow!("Expected UInt16"))
332 }
333}
334
335pub fn decode_access_control_entries_per_fabric(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
337 if let tlv::TlvItemValue::Int(v) = inp {
338 Ok(*v as u16)
339 } else {
340 Err(anyhow::anyhow!("Expected UInt16"))
341 }
342}
343
344pub fn decode_commissioning_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CommissioningAccessRestrictionEntry>> {
346 let mut res = Vec::new();
347 if let tlv::TlvItemValue::List(v) = inp {
348 for item in v {
349 res.push(CommissioningAccessRestrictionEntry {
350 endpoint: item.get_int(&[0]).map(|v| v as u16),
351 cluster: item.get_int(&[1]).map(|v| v as u32),
352 restrictions: {
353 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
354 let mut items = Vec::new();
355 for list_item in l {
356 items.push(AccessRestriction {
357 type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
358 id: list_item.get_int(&[1]).map(|v| v as u32),
359 });
360 }
361 Some(items)
362 } else {
363 None
364 }
365 },
366 });
367 }
368 }
369 Ok(res)
370}
371
372pub fn decode_arl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessRestrictionEntry>> {
374 let mut res = Vec::new();
375 if let tlv::TlvItemValue::List(v) = inp {
376 for item in v {
377 res.push(AccessRestrictionEntry {
378 endpoint: item.get_int(&[0]).map(|v| v as u16),
379 cluster: item.get_int(&[1]).map(|v| v as u32),
380 restrictions: {
381 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
382 let mut items = Vec::new();
383 for list_item in l {
384 items.push(AccessRestriction {
385 type_: list_item.get_int(&[0]).and_then(|v| AccessRestrictionType::from_u8(v as u8)),
386 id: list_item.get_int(&[1]).map(|v| v as u32),
387 });
388 }
389 Some(items)
390 } else {
391 None
392 }
393 },
394 });
395 }
396 }
397 Ok(res)
398}
399
400pub fn decode_auxiliary_acl(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<AccessControlEntry>> {
402 let mut res = Vec::new();
403 if let tlv::TlvItemValue::List(v) = inp {
404 for item in v {
405 res.push(AccessControlEntry {
406 privilege: item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
407 auth_mode: item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
408 subjects: {
409 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[3]) {
410 let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
411 Some(items)
412 } else {
413 None
414 }
415 },
416 targets: {
417 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
418 let mut items = Vec::new();
419 for list_item in l {
420 items.push(AccessControlTarget {
421 cluster: list_item.get_int(&[0]).map(|v| v as u32),
422 endpoint: list_item.get_int(&[1]).map(|v| v as u16),
423 device_type: list_item.get_int(&[2]).map(|v| v as u32),
424 });
425 }
426 Some(items)
427 } else {
428 None
429 }
430 },
431 auxiliary_type: item.get_int(&[5]).and_then(|v| AccessControlAuxiliaryType::from_u8(v as u8)),
432 });
433 }
434 }
435 Ok(res)
436}
437
438
439pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
451 if cluster_id != 0x001F {
453 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x001F, got {}\"}}", cluster_id);
454 }
455
456 match attribute_id {
457 0x0000 => {
458 match decode_acl(tlv_value) {
459 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
460 Err(e) => format!("{{\"error\": \"{}\"}}", e),
461 }
462 }
463 0x0001 => {
464 match decode_extension(tlv_value) {
465 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
466 Err(e) => format!("{{\"error\": \"{}\"}}", e),
467 }
468 }
469 0x0002 => {
470 match decode_subjects_per_access_control_entry(tlv_value) {
471 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
472 Err(e) => format!("{{\"error\": \"{}\"}}", e),
473 }
474 }
475 0x0003 => {
476 match decode_targets_per_access_control_entry(tlv_value) {
477 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
478 Err(e) => format!("{{\"error\": \"{}\"}}", e),
479 }
480 }
481 0x0004 => {
482 match decode_access_control_entries_per_fabric(tlv_value) {
483 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
484 Err(e) => format!("{{\"error\": \"{}\"}}", e),
485 }
486 }
487 0x0005 => {
488 match decode_commissioning_arl(tlv_value) {
489 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
490 Err(e) => format!("{{\"error\": \"{}\"}}", e),
491 }
492 }
493 0x0006 => {
494 match decode_arl(tlv_value) {
495 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
496 Err(e) => format!("{{\"error\": \"{}\"}}", e),
497 }
498 }
499 0x0007 => {
500 match decode_auxiliary_acl(tlv_value) {
501 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
502 Err(e) => format!("{{\"error\": \"{}\"}}", e),
503 }
504 }
505 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
506 }
507}
508
509pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
514 vec![
515 (0x0000, "ACL"),
516 (0x0001, "Extension"),
517 (0x0002, "SubjectsPerAccessControlEntry"),
518 (0x0003, "TargetsPerAccessControlEntry"),
519 (0x0004, "AccessControlEntriesPerFabric"),
520 (0x0005, "CommissioningARL"),
521 (0x0006, "ARL"),
522 (0x0007, "AuxiliaryACL"),
523 ]
524}
525
526pub fn get_command_list() -> Vec<(u32, &'static str)> {
529 vec![
530 (0x00, "ReviewFabricRestrictions"),
531 ]
532}
533
534pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
535 match cmd_id {
536 0x00 => Some("ReviewFabricRestrictions"),
537 _ => None,
538 }
539}
540
541pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
542 match cmd_id {
543 0x00 => Some(vec![
544 crate::clusters::codec::CommandField { tag: 0, name: "arl", kind: crate::clusters::codec::FieldKind::List { entry_type: "CommissioningAccessRestrictionEntryStruct" }, optional: false, nullable: false },
545 ]),
546 _ => None,
547 }
548}
549
550pub fn encode_command_json(cmd_id: u32, _args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
551 match cmd_id {
552 0x00 => Err(anyhow::anyhow!("command \"ReviewFabricRestrictions\" has complex args: use raw mode")),
553 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
554 }
555}
556
557#[derive(Debug, serde::Serialize)]
558pub struct ReviewFabricRestrictionsResponse {
559 pub token: Option<u64>,
560}
561
562pub fn decode_review_fabric_restrictions_response(inp: &tlv::TlvItemValue) -> anyhow::Result<ReviewFabricRestrictionsResponse> {
566 if let tlv::TlvItemValue::List(_fields) = inp {
567 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
568 Ok(ReviewFabricRestrictionsResponse {
569 token: item.get_int(&[0]),
570 })
571 } else {
572 Err(anyhow::anyhow!("Expected struct fields"))
573 }
574}
575
576pub async fn review_fabric_restrictions(conn: &crate::controller::Connection, endpoint: u16, arl: Vec<CommissioningAccessRestrictionEntry>) -> anyhow::Result<ReviewFabricRestrictionsResponse> {
580 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_CMD_ID_REVIEWFABRICRESTRICTIONS, &encode_review_fabric_restrictions(arl)?).await?;
581 decode_review_fabric_restrictions_response(&tlv)
582}
583
584pub async fn read_acl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessControlEntry>> {
586 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_ACL).await?;
587 decode_acl(&tlv)
588}
589
590pub async fn read_extension(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessControlExtension>> {
592 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_EXTENSION).await?;
593 decode_extension(&tlv)
594}
595
596pub async fn read_subjects_per_access_control_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
598 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_SUBJECTSPERACCESSCONTROLENTRY).await?;
599 decode_subjects_per_access_control_entry(&tlv)
600}
601
602pub async fn read_targets_per_access_control_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
604 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_TARGETSPERACCESSCONTROLENTRY).await?;
605 decode_targets_per_access_control_entry(&tlv)
606}
607
608pub async fn read_access_control_entries_per_fabric(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
610 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_ACCESSCONTROLENTRIESPERFABRIC).await?;
611 decode_access_control_entries_per_fabric(&tlv)
612}
613
614pub async fn read_commissioning_arl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<CommissioningAccessRestrictionEntry>> {
616 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_COMMISSIONINGARL).await?;
617 decode_commissioning_arl(&tlv)
618}
619
620pub async fn read_arl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessRestrictionEntry>> {
622 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_ARL).await?;
623 decode_arl(&tlv)
624}
625
626pub async fn read_auxiliary_acl(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<AccessControlEntry>> {
628 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ACCESS_CONTROL, crate::clusters::defs::CLUSTER_ACCESS_CONTROL_ATTR_ID_AUXILIARYACL).await?;
629 decode_auxiliary_acl(&tlv)
630}
631
632#[derive(Debug, serde::Serialize)]
633pub struct AccessControlEntryChangedEvent {
634 pub admin_node_id: Option<u64>,
635 pub admin_passcode_id: Option<u16>,
636 pub change_type: Option<ChangeType>,
637 pub latest_value: Option<AccessControlEntry>,
638}
639
640#[derive(Debug, serde::Serialize)]
641pub struct AccessControlExtensionChangedEvent {
642 pub admin_node_id: Option<u64>,
643 pub admin_passcode_id: Option<u16>,
644 pub change_type: Option<ChangeType>,
645 pub latest_value: Option<AccessControlExtension>,
646}
647
648#[derive(Debug, serde::Serialize)]
649pub struct FabricRestrictionReviewUpdateEvent {
650 pub token: Option<u64>,
651 pub instruction: Option<String>,
652 pub arl_request_flow_url: Option<String>,
653}
654
655#[derive(Debug, serde::Serialize)]
656pub struct AuxiliaryAccessUpdatedEvent {
657 pub admin_node_id: Option<u64>,
658}
659
660pub fn decode_access_control_entry_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlEntryChangedEvent> {
664 if let tlv::TlvItemValue::List(_fields) = inp {
665 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
666 Ok(AccessControlEntryChangedEvent {
667 admin_node_id: item.get_int(&[1]),
668 admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
669 change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
670 latest_value: {
671 if let Some(nested_tlv) = item.get(&[4]) {
672 if let tlv::TlvItemValue::List(_) = nested_tlv {
673 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
674 Some(AccessControlEntry {
675 privilege: nested_item.get_int(&[1]).and_then(|v| AccessControlEntryPrivilege::from_u8(v as u8)),
676 auth_mode: nested_item.get_int(&[2]).and_then(|v| AccessControlEntryAuthMode::from_u8(v as u8)),
677 subjects: {
678 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[3]) {
679 let items: Vec<u64> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v) } else { None } }).collect();
680 Some(items)
681 } else {
682 None
683 }
684 },
685 targets: {
686 if let Some(tlv::TlvItemValue::List(l)) = nested_item.get(&[4]) {
687 let mut items = Vec::new();
688 for list_item in l {
689 items.push(AccessControlTarget {
690 cluster: list_item.get_int(&[0]).map(|v| v as u32),
691 endpoint: list_item.get_int(&[1]).map(|v| v as u16),
692 device_type: list_item.get_int(&[2]).map(|v| v as u32),
693 });
694 }
695 Some(items)
696 } else {
697 None
698 }
699 },
700 auxiliary_type: nested_item.get_int(&[5]).and_then(|v| AccessControlAuxiliaryType::from_u8(v as u8)),
701 })
702 } else {
703 None
704 }
705 } else {
706 None
707 }
708 },
709 })
710 } else {
711 Err(anyhow::anyhow!("Expected struct fields"))
712 }
713}
714
715pub fn decode_access_control_extension_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AccessControlExtensionChangedEvent> {
717 if let tlv::TlvItemValue::List(_fields) = inp {
718 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
719 Ok(AccessControlExtensionChangedEvent {
720 admin_node_id: item.get_int(&[1]),
721 admin_passcode_id: item.get_int(&[2]).map(|v| v as u16),
722 change_type: item.get_int(&[3]).and_then(|v| ChangeType::from_u8(v as u8)),
723 latest_value: {
724 if let Some(nested_tlv) = item.get(&[4]) {
725 if let tlv::TlvItemValue::List(_) = nested_tlv {
726 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
727 Some(AccessControlExtension {
728 data: nested_item.get_octet_string_owned(&[1]),
729 })
730 } else {
731 None
732 }
733 } else {
734 None
735 }
736 },
737 })
738 } else {
739 Err(anyhow::anyhow!("Expected struct fields"))
740 }
741}
742
743pub fn decode_fabric_restriction_review_update_event(inp: &tlv::TlvItemValue) -> anyhow::Result<FabricRestrictionReviewUpdateEvent> {
745 if let tlv::TlvItemValue::List(_fields) = inp {
746 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
747 Ok(FabricRestrictionReviewUpdateEvent {
748 token: item.get_int(&[0]),
749 instruction: item.get_string_owned(&[1]),
750 arl_request_flow_url: item.get_string_owned(&[2]),
751 })
752 } else {
753 Err(anyhow::anyhow!("Expected struct fields"))
754 }
755}
756
757pub fn decode_auxiliary_access_updated_event(inp: &tlv::TlvItemValue) -> anyhow::Result<AuxiliaryAccessUpdatedEvent> {
759 if let tlv::TlvItemValue::List(_fields) = inp {
760 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
761 Ok(AuxiliaryAccessUpdatedEvent {
762 admin_node_id: item.get_int(&[0]),
763 })
764 } else {
765 Err(anyhow::anyhow!("Expected struct fields"))
766 }
767}
768
769
770pub fn decode_event_json(cluster_id: u32, event_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
774 if cluster_id != 0x001F {
775 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x001F, got {}\"}}", cluster_id);
776 }
777
778 match event_id {
779 0x00 => {
780 match decode_access_control_entry_changed_event(tlv_value) {
781 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
782 Err(e) => format!("{{\"error\": \"{}\"}}", e),
783 }
784 }
785 0x01 => {
786 match decode_access_control_extension_changed_event(tlv_value) {
787 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
788 Err(e) => format!("{{\"error\": \"{}\"}}", e),
789 }
790 }
791 0x02 => {
792 match decode_fabric_restriction_review_update_event(tlv_value) {
793 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
794 Err(e) => format!("{{\"error\": \"{}\"}}", e),
795 }
796 }
797 0x03 => {
798 match decode_auxiliary_access_updated_event(tlv_value) {
799 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
800 Err(e) => format!("{{\"error\": \"{}\"}}", e),
801 }
802 }
803 _ => format!("{{\"error\": \"Unknown event ID: {}\"}}", event_id),
804 }
805}
806
807pub fn get_event_list() -> Vec<(u32, &'static str)> {
812 vec![
813 (0x00, "AccessControlEntryChanged"),
814 (0x01, "AccessControlExtensionChanged"),
815 (0x02, "FabricRestrictionReviewUpdate"),
816 (0x03, "AuxiliaryAccessUpdated"),
817 ]
818}
819