1#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum HapAdvert {
9 Regular {
11 device_id: [u8; 6],
13 gsn: u16,
15 paired: bool,
17 },
18 EncryptedNotification {
20 advertising_id: [u8; 6],
22 payload: Vec<u8>,
24 },
25}
26
27impl HapAdvert {
28 #[must_use]
31 pub fn parse(mfg: &[u8]) -> Option<Self> {
32 match mfg.first()? {
33 0x06 if mfg.len() >= 13 => {
34 let mut device_id = [0u8; 6];
35 device_id.copy_from_slice(&mfg[3..9]);
36 let gsn = u16::from_le_bytes([mfg[11], mfg[12]]);
37 let paired = mfg[2] & 0x01 == 0;
38 Some(Self::Regular {
39 device_id,
40 gsn,
41 paired,
42 })
43 }
44 0x11 if mfg.len() >= 8 => {
45 let mut advertising_id = [0u8; 6];
46 advertising_id.copy_from_slice(&mfg[2..8]);
47 Some(Self::EncryptedNotification {
48 advertising_id,
49 payload: mfg[8..].to_vec(),
50 })
51 }
52 _ => None,
53 }
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 #[test]
61 #[allow(clippy::unwrap_used)]
62 fn parses_regular_0x06_advert() {
63 let mfg = [
64 0x06, 0x21, 0x01, 1, 2, 3, 4, 5, 6, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00,
65 ];
66 let a = HapAdvert::parse(&mfg).unwrap();
67 match a {
68 HapAdvert::Regular {
69 device_id,
70 gsn,
71 paired,
72 } => {
73 assert_eq!(device_id, [1, 2, 3, 4, 5, 6]);
74 assert_eq!(gsn, 5);
75 assert!(!paired);
76 }
77 HapAdvert::EncryptedNotification { .. } => panic!("expected Regular"),
78 }
79 }
80 #[test]
81 #[allow(clippy::unwrap_used)]
82 fn parses_encrypted_0x11_advert() {
83 let mfg = [0x11, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 9, 9, 9];
84 let a = HapAdvert::parse(&mfg).unwrap();
85 match a {
86 HapAdvert::EncryptedNotification {
87 advertising_id,
88 payload,
89 } => {
90 assert_eq!(advertising_id, [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
91 assert_eq!(payload, vec![9, 9, 9]);
92 }
93 HapAdvert::Regular { .. } => panic!("expected EncryptedNotification"),
94 }
95 }
96 #[test]
97 fn rejects_short_advert() {
98 assert!(HapAdvert::parse(&[0x06, 0x21]).is_none());
99 }
100}