1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum GroupcastTestResult {
18 Success = 0,
20 Generalerror = 1,
22 Messagereplay = 2,
24 Failedauth = 3,
26 Noavailablekey = 4,
28 Sendfailure = 5,
30}
31
32impl GroupcastTestResult {
33 pub fn from_u8(value: u8) -> Option<Self> {
35 match value {
36 0 => Some(GroupcastTestResult::Success),
37 1 => Some(GroupcastTestResult::Generalerror),
38 2 => Some(GroupcastTestResult::Messagereplay),
39 3 => Some(GroupcastTestResult::Failedauth),
40 4 => Some(GroupcastTestResult::Noavailablekey),
41 5 => Some(GroupcastTestResult::Sendfailure),
42 _ => None,
43 }
44 }
45
46 pub fn to_u8(self) -> u8 {
48 self as u8
49 }
50}
51
52impl From<GroupcastTestResult> for u8 {
53 fn from(val: GroupcastTestResult) -> Self {
54 val as u8
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
59#[repr(u8)]
60pub enum GroupcastTesting {
61 Disabletesting = 0,
63 Enablelistenertesting = 1,
65 Enablesendertesting = 2,
67}
68
69impl GroupcastTesting {
70 pub fn from_u8(value: u8) -> Option<Self> {
72 match value {
73 0 => Some(GroupcastTesting::Disabletesting),
74 1 => Some(GroupcastTesting::Enablelistenertesting),
75 2 => Some(GroupcastTesting::Enablesendertesting),
76 _ => None,
77 }
78 }
79
80 pub fn to_u8(self) -> u8 {
82 self as u8
83 }
84}
85
86impl From<GroupcastTesting> for u8 {
87 fn from(val: GroupcastTesting) -> Self {
88 val as u8
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
93#[repr(u8)]
94pub enum MulticastAddrPolicy {
95 Ianaaddr = 0,
97 Pergroup = 1,
99}
100
101impl MulticastAddrPolicy {
102 pub fn from_u8(value: u8) -> Option<Self> {
104 match value {
105 0 => Some(MulticastAddrPolicy::Ianaaddr),
106 1 => Some(MulticastAddrPolicy::Pergroup),
107 _ => None,
108 }
109 }
110
111 pub fn to_u8(self) -> u8 {
113 self as u8
114 }
115}
116
117impl From<MulticastAddrPolicy> for u8 {
118 fn from(val: MulticastAddrPolicy) -> Self {
119 val as u8
120 }
121}
122
123#[derive(Debug, serde::Serialize)]
126pub struct Membership {
127 pub group_id: Option<u16>,
128 pub endpoints: Option<Vec<u16>>,
129 pub key_set_id: Option<u16>,
130 pub has_auxiliary_acl: Option<bool>,
131 pub mcast_addr_policy: Option<MulticastAddrPolicy>,
132}
133
134pub fn encode_join_group(group_id: u16, endpoints: Vec<u16>, key_set_id: u16, key: Option<Vec<u8>>, use_auxiliary_acl: Option<bool>, replace_endpoints: Option<bool>, mcast_addr_policy: Option<MulticastAddrPolicy>) -> anyhow::Result<Vec<u8>> {
138 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
139 tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(group_id)).into());
140 tlv_fields.push((1, tlv::TlvItemValueEnc::StructAnon(endpoints.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt16(v)).into()).collect())).into());
141 tlv_fields.push((2, tlv::TlvItemValueEnc::UInt16(key_set_id)).into());
142 if let Some(x) = key { tlv_fields.push((3, tlv::TlvItemValueEnc::OctetString(x)).into()); }
143 if let Some(x) = use_auxiliary_acl { tlv_fields.push((4, tlv::TlvItemValueEnc::Bool(x)).into()); }
144 if let Some(x) = replace_endpoints { tlv_fields.push((5, tlv::TlvItemValueEnc::Bool(x)).into()); }
145 if let Some(x) = mcast_addr_policy { tlv_fields.push((6, tlv::TlvItemValueEnc::UInt8(x.to_u8())).into()); }
146 let tlv = tlv::TlvItemEnc {
147 tag: 0,
148 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
149 };
150 Ok(tlv.encode()?)
151}
152
153pub fn encode_leave_group(group_id: u16, endpoints: Option<Vec<u16>>) -> anyhow::Result<Vec<u8>> {
155 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
156 tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(group_id)).into());
157 if let Some(endpoints) = endpoints {
158 tlv_fields.push((1, tlv::TlvItemValueEnc::StructAnon(endpoints.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt16(v)).into()).collect())).into());
159 }
160 let tlv = tlv::TlvItemEnc {
161 tag: 0,
162 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
163 };
164 Ok(tlv.encode()?)
165}
166
167pub fn encode_update_group_key(group_id: u16, key_set_id: u16, key: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
169 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
170 tlv_fields.push((0, tlv::TlvItemValueEnc::UInt16(group_id)).into());
171 tlv_fields.push((1, tlv::TlvItemValueEnc::UInt16(key_set_id)).into());
172 if let Some(x) = key { tlv_fields.push((2, tlv::TlvItemValueEnc::OctetString(x)).into()); }
173 let tlv = tlv::TlvItemEnc {
174 tag: 0,
175 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
176 };
177 Ok(tlv.encode()?)
178}
179
180pub fn encode_configure_auxiliary_acl(group_id: u16, use_auxiliary_acl: bool) -> anyhow::Result<Vec<u8>> {
182 let tlv = tlv::TlvItemEnc {
183 tag: 0,
184 value: tlv::TlvItemValueEnc::StructInvisible(vec![
185 (0, tlv::TlvItemValueEnc::UInt16(group_id)).into(),
186 (1, tlv::TlvItemValueEnc::Bool(use_auxiliary_acl)).into(),
187 ]),
188 };
189 Ok(tlv.encode()?)
190}
191
192pub fn encode_groupcast_testing(test_operation: GroupcastTesting, duration_seconds: Option<u16>) -> anyhow::Result<Vec<u8>> {
194 let mut tlv_fields: Vec<tlv::TlvItemEnc> = Vec::new();
195 tlv_fields.push((0, tlv::TlvItemValueEnc::UInt8(test_operation.to_u8())).into());
196 if let Some(x) = duration_seconds { tlv_fields.push((1, tlv::TlvItemValueEnc::UInt16(x)).into()); }
197 let tlv = tlv::TlvItemEnc {
198 tag: 0,
199 value: tlv::TlvItemValueEnc::StructInvisible(tlv_fields),
200 };
201 Ok(tlv.encode()?)
202}
203
204pub fn decode_membership(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Membership>> {
208 let mut res = Vec::new();
209 if let tlv::TlvItemValue::List(v) = inp {
210 for item in v {
211 res.push(Membership {
212 group_id: item.get_int(&[0]).map(|v| v as u16),
213 endpoints: {
214 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
215 let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
216 Some(items)
217 } else {
218 None
219 }
220 },
221 key_set_id: item.get_int(&[2]).map(|v| v as u16),
222 has_auxiliary_acl: item.get_bool(&[3]),
223 mcast_addr_policy: item.get_int(&[4]).and_then(|v| MulticastAddrPolicy::from_u8(v as u8)),
224 });
225 }
226 }
227 Ok(res)
228}
229
230pub fn decode_max_membership_count(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
232 if let tlv::TlvItemValue::Int(v) = inp {
233 Ok(*v as u16)
234 } else {
235 Err(anyhow::anyhow!("Expected UInt16"))
236 }
237}
238
239pub fn decode_max_mcast_addr_count(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
241 if let tlv::TlvItemValue::Int(v) = inp {
242 Ok(*v as u16)
243 } else {
244 Err(anyhow::anyhow!("Expected UInt16"))
245 }
246}
247
248pub fn decode_used_mcast_addr_count(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
250 if let tlv::TlvItemValue::Int(v) = inp {
251 Ok(*v as u16)
252 } else {
253 Err(anyhow::anyhow!("Expected UInt16"))
254 }
255}
256
257pub fn decode_fabric_under_test(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
259 if let tlv::TlvItemValue::Int(v) = inp {
260 Ok(*v as u8)
261 } else {
262 Err(anyhow::anyhow!("Expected UInt8"))
263 }
264}
265
266
267pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
279 if cluster_id != 0x0065 {
281 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0065, got {}\"}}", cluster_id);
282 }
283
284 match attribute_id {
285 0x0000 => {
286 match decode_membership(tlv_value) {
287 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
288 Err(e) => format!("{{\"error\": \"{}\"}}", e),
289 }
290 }
291 0x0001 => {
292 match decode_max_membership_count(tlv_value) {
293 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
294 Err(e) => format!("{{\"error\": \"{}\"}}", e),
295 }
296 }
297 0x0002 => {
298 match decode_max_mcast_addr_count(tlv_value) {
299 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
300 Err(e) => format!("{{\"error\": \"{}\"}}", e),
301 }
302 }
303 0x0003 => {
304 match decode_used_mcast_addr_count(tlv_value) {
305 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
306 Err(e) => format!("{{\"error\": \"{}\"}}", e),
307 }
308 }
309 0x0004 => {
310 match decode_fabric_under_test(tlv_value) {
311 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
312 Err(e) => format!("{{\"error\": \"{}\"}}", e),
313 }
314 }
315 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
316 }
317}
318
319pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
324 vec![
325 (0x0000, "Membership"),
326 (0x0001, "MaxMembershipCount"),
327 (0x0002, "MaxMcastAddrCount"),
328 (0x0003, "UsedMcastAddrCount"),
329 (0x0004, "FabricUnderTest"),
330 ]
331}
332
333pub fn get_command_list() -> Vec<(u32, &'static str)> {
336 vec![
337 (0x00, "JoinGroup"),
338 (0x01, "LeaveGroup"),
339 (0x03, "UpdateGroupKey"),
340 (0x04, "ConfigureAuxiliaryACL"),
341 (0x05, "GroupcastTesting"),
342 ]
343}
344
345pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
346 match cmd_id {
347 0x00 => Some("JoinGroup"),
348 0x01 => Some("LeaveGroup"),
349 0x03 => Some("UpdateGroupKey"),
350 0x04 => Some("ConfigureAuxiliaryACL"),
351 0x05 => Some("GroupcastTesting"),
352 _ => None,
353 }
354}
355
356pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
357 match cmd_id {
358 0x00 => Some(vec![
359 crate::clusters::codec::CommandField { tag: 0, name: "group_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
360 crate::clusters::codec::CommandField { tag: 1, name: "endpoints", kind: crate::clusters::codec::FieldKind::List { entry_type: "endpoint-no" }, optional: false, nullable: false },
361 crate::clusters::codec::CommandField { tag: 2, name: "key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
362 crate::clusters::codec::CommandField { tag: 3, name: "key", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
363 crate::clusters::codec::CommandField { tag: 4, name: "use_auxiliary_acl", kind: crate::clusters::codec::FieldKind::Bool, optional: true, nullable: false },
364 crate::clusters::codec::CommandField { tag: 5, name: "replace_endpoints", kind: crate::clusters::codec::FieldKind::Bool, optional: true, nullable: false },
365 crate::clusters::codec::CommandField { tag: 6, name: "mcast_addr_policy", kind: crate::clusters::codec::FieldKind::Enum { name: "MulticastAddrPolicy", variants: &[(0, "Ianaaddr"), (1, "Pergroup")] }, optional: true, nullable: false },
366 ]),
367 0x01 => Some(vec![
368 crate::clusters::codec::CommandField { tag: 0, name: "group_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
369 crate::clusters::codec::CommandField { tag: 1, name: "endpoints", kind: crate::clusters::codec::FieldKind::List { entry_type: "endpoint-no" }, optional: true, nullable: false },
370 ]),
371 0x03 => Some(vec![
372 crate::clusters::codec::CommandField { tag: 0, name: "group_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
373 crate::clusters::codec::CommandField { tag: 1, name: "key_set_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
374 crate::clusters::codec::CommandField { tag: 2, name: "key", kind: crate::clusters::codec::FieldKind::OctetString, optional: true, nullable: false },
375 ]),
376 0x04 => Some(vec![
377 crate::clusters::codec::CommandField { tag: 0, name: "group_id", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
378 crate::clusters::codec::CommandField { tag: 1, name: "use_auxiliary_acl", kind: crate::clusters::codec::FieldKind::Bool, optional: false, nullable: false },
379 ]),
380 0x05 => Some(vec![
381 crate::clusters::codec::CommandField { tag: 0, name: "test_operation", kind: crate::clusters::codec::FieldKind::Enum { name: "GroupcastTesting", variants: &[(0, "Disabletesting"), (1, "Enablelistenertesting"), (2, "Enablesendertesting")] }, optional: false, nullable: false },
382 crate::clusters::codec::CommandField { tag: 1, name: "duration_seconds", kind: crate::clusters::codec::FieldKind::U16, optional: true, nullable: false },
383 ]),
384 _ => None,
385 }
386}
387
388pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
389 match cmd_id {
390 0x00 => Err(anyhow::anyhow!("command \"JoinGroup\" has complex args: use raw mode")),
391 0x01 => Err(anyhow::anyhow!("command \"LeaveGroup\" has complex args: use raw mode")),
392 0x03 => {
393 let group_id = crate::clusters::codec::json_util::get_u16(args, "group_id")?;
394 let key_set_id = crate::clusters::codec::json_util::get_u16(args, "key_set_id")?;
395 let key = crate::clusters::codec::json_util::get_opt_octstr(args, "key")?;
396 encode_update_group_key(group_id, key_set_id, key)
397 }
398 0x04 => {
399 let group_id = crate::clusters::codec::json_util::get_u16(args, "group_id")?;
400 let use_auxiliary_acl = crate::clusters::codec::json_util::get_bool(args, "use_auxiliary_acl")?;
401 encode_configure_auxiliary_acl(group_id, use_auxiliary_acl)
402 }
403 0x05 => {
404 let test_operation = {
405 let n = crate::clusters::codec::json_util::get_u64(args, "test_operation")?;
406 GroupcastTesting::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid GroupcastTesting: {}", n))?
407 };
408 let duration_seconds = crate::clusters::codec::json_util::get_opt_u16(args, "duration_seconds")?;
409 encode_groupcast_testing(test_operation, duration_seconds)
410 }
411 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
412 }
413}
414
415#[derive(Debug, serde::Serialize)]
416pub struct LeaveGroupResponse {
417 pub group_id: Option<u16>,
418 pub endpoints: Option<Vec<u16>>,
419}
420
421pub fn decode_leave_group_response(inp: &tlv::TlvItemValue) -> anyhow::Result<LeaveGroupResponse> {
425 if let tlv::TlvItemValue::List(_fields) = inp {
426 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
427 Ok(LeaveGroupResponse {
428 group_id: item.get_int(&[0]).map(|v| v as u16),
429 endpoints: {
430 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
431 let items: Vec<u16> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u16) } else { None } }).collect();
432 Some(items)
433 } else {
434 None
435 }
436 },
437 })
438 } else {
439 Err(anyhow::anyhow!("Expected struct fields"))
440 }
441}
442
443pub async fn join_group(conn: &crate::controller::Connection, endpoint: u16, group_id: u16, endpoints: Vec<u16>, key_set_id: u16, key: Option<Vec<u8>>, use_auxiliary_acl: Option<bool>, replace_endpoints: Option<bool>, mcast_addr_policy: Option<MulticastAddrPolicy>) -> anyhow::Result<()> {
447 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_CMD_ID_JOINGROUP, &encode_join_group(group_id, endpoints, key_set_id, key, use_auxiliary_acl, replace_endpoints, mcast_addr_policy)?).await?;
448 Ok(())
449}
450
451pub async fn leave_group(conn: &crate::controller::Connection, endpoint: u16, group_id: u16, endpoints: Option<Vec<u16>>) -> anyhow::Result<LeaveGroupResponse> {
453 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_CMD_ID_LEAVEGROUP, &encode_leave_group(group_id, endpoints)?).await?;
454 decode_leave_group_response(&tlv)
455}
456
457pub async fn update_group_key(conn: &crate::controller::Connection, endpoint: u16, group_id: u16, key_set_id: u16, key: Option<Vec<u8>>) -> anyhow::Result<()> {
459 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_CMD_ID_UPDATEGROUPKEY, &encode_update_group_key(group_id, key_set_id, key)?).await?;
460 Ok(())
461}
462
463pub async fn configure_auxiliary_acl(conn: &crate::controller::Connection, endpoint: u16, group_id: u16, use_auxiliary_acl: bool) -> anyhow::Result<()> {
465 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_CMD_ID_CONFIGUREAUXILIARYACL, &encode_configure_auxiliary_acl(group_id, use_auxiliary_acl)?).await?;
466 Ok(())
467}
468
469pub async fn groupcast_testing(conn: &crate::controller::Connection, endpoint: u16, test_operation: GroupcastTesting, duration_seconds: Option<u16>) -> anyhow::Result<()> {
471 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_CMD_ID_GROUPCASTTESTING, &encode_groupcast_testing(test_operation, duration_seconds)?).await?;
472 Ok(())
473}
474
475pub async fn read_membership(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Membership>> {
477 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_ATTR_ID_MEMBERSHIP).await?;
478 decode_membership(&tlv)
479}
480
481pub async fn read_max_membership_count(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
483 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_ATTR_ID_MAXMEMBERSHIPCOUNT).await?;
484 decode_max_membership_count(&tlv)
485}
486
487pub async fn read_max_mcast_addr_count(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
489 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_ATTR_ID_MAXMCASTADDRCOUNT).await?;
490 decode_max_mcast_addr_count(&tlv)
491}
492
493pub async fn read_used_mcast_addr_count(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
495 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_ATTR_ID_USEDMCASTADDRCOUNT).await?;
496 decode_used_mcast_addr_count(&tlv)
497}
498
499pub async fn read_fabric_under_test(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
501 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_GROUPCAST, crate::clusters::defs::CLUSTER_GROUPCAST_ATTR_ID_FABRICUNDERTEST).await?;
502 decode_fabric_under_test(&tlv)
503}
504
505#[derive(Debug, serde::Serialize)]
506pub struct GroupcastTestingEvent {
507 pub source_ip_address: Option<u8>,
508 pub destination_ip_address: Option<u8>,
509 pub group_id: Option<u16>,
510 pub endpoint_id: Option<u8>,
511 pub cluster_id: Option<u32>,
512 pub element_id: Option<u32>,
513 pub access_allowed: Option<bool>,
514 pub groupcast_test_result: Option<GroupcastTestResult>,
515}
516
517pub fn decode_groupcast_testing_event(inp: &tlv::TlvItemValue) -> anyhow::Result<GroupcastTestingEvent> {
521 if let tlv::TlvItemValue::List(_fields) = inp {
522 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
523 Ok(GroupcastTestingEvent {
524 source_ip_address: item.get_int(&[0]).map(|v| v as u8),
525 destination_ip_address: item.get_int(&[1]).map(|v| v as u8),
526 group_id: item.get_int(&[2]).map(|v| v as u16),
527 endpoint_id: item.get_int(&[3]).map(|v| v as u8),
528 cluster_id: item.get_int(&[4]).map(|v| v as u32),
529 element_id: item.get_int(&[5]).map(|v| v as u32),
530 access_allowed: item.get_bool(&[6]),
531 groupcast_test_result: item.get_int(&[7]).and_then(|v| GroupcastTestResult::from_u8(v as u8)),
532 })
533 } else {
534 Err(anyhow::anyhow!("Expected struct fields"))
535 }
536}
537
538
539pub fn decode_event_json(cluster_id: u32, event_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
543 if cluster_id != 0x0065 {
544 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0065, got {}\"}}", cluster_id);
545 }
546
547 match event_id {
548 0x00 => {
549 match decode_groupcast_testing_event(tlv_value) {
550 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
551 Err(e) => format!("{{\"error\": \"{}\"}}", e),
552 }
553 }
554 _ => format!("{{\"error\": \"Unknown event ID: {}\"}}", event_id),
555 }
556}
557
558pub fn get_event_list() -> Vec<(u32, &'static str)> {
563 vec![
564 (0x00, "GroupcastTesting"),
565 ]
566}
567