homekit/
advertise.rs

1use {
2    byteorder::{self, ByteOrder},
3    rubble::link::ad_structure::AdStructure,
4};
5
6pub struct Data([u8; 17]);
7
8impl<'a, 'b> Data
9where
10    'a: 'b,
11{
12    pub fn new(
13        interval: Interval,
14        pair_status: PairingStatus,
15        device_id: u64,
16        accessory_category: AccessoryCategory,
17        global_state_number: u16,
18        configuration_number: u8,
19    ) -> Self {
20        let mut inner = [0u8; 17];
21
22        // CoID of Apple, Inc.
23        inner[0] = 0x4C;
24        inner[1] = 0x00;
25
26        // Type (HomeKit)
27        inner[2] = 0x06;
28
29        // Advertising Interval and Length
30        inner[3] = interval.value();
31
32        // Status Flags
33        inner[4] = pair_status.value();
34
35        // 48-bit Device ID
36        byteorder::BigEndian::write_u48(&mut inner[5..12], device_id);
37
38        // Accessory Category Identifier
39        byteorder::BigEndian::write_u16(&mut inner[11..13], accessory_category.value());
40
41        // Global State Number
42        byteorder::BigEndian::write_u16(&mut inner[13..15], global_state_number);
43
44        // Configuration Number
45        inner[15] = configuration_number;
46
47        // Compatible Version
48        inner[16] = 0x02;
49
50        Self(inner)
51    }
52
53    pub fn as_adstructure(&'a self) -> AdStructure<'b> {
54        AdStructure::Unknown {
55            ty: 0xFF,
56            data: &self.0,
57        }
58    }
59}
60
61pub enum Interval {
62    _10_25MS,
63    _26_100MS,
64    _101_300MS,
65    _301_500MS,
66    _501_1250MS,
67    _1251_2500MS,
68    _2500MS,
69}
70
71impl Interval {
72    fn value(&self) -> u8 {
73        match *self {
74            Interval::_10_25MS => 0x2D,
75            Interval::_26_100MS => 0x4D,
76            Interval::_101_300MS => 0x6D,
77            Interval::_301_500MS => 0x8D,
78            Interval::_501_1250MS => 0xAD,
79            Interval::_1251_2500MS => 0xCD,
80            Interval::_2500MS => 0xED,
81        }
82    }
83}
84
85pub enum PairingStatus {
86    Paired,
87    NotPaired,
88}
89
90impl PairingStatus {
91    fn value(&self) -> u8 {
92        match *self {
93            PairingStatus::Paired => 0b0,
94            PairingStatus::NotPaired => 0b1,
95        }
96    }
97}
98
99pub enum AccessoryCategory {
100    Other,
101    Bridge,
102    Fan,
103    Garage,
104    Lightbulb,
105    DoorLock,
106    Outlet,
107    Switch,
108    Thermostat,
109    Sensor,
110    SecuritySystem,
111    Door,
112    Window,
113    WindowCovering,
114    ProgrammableSwitch,
115    RangeExtender,
116    IPCamera,
117    VideoDoorBell,
118    AirPurifier,
119    Reserved,
120}
121
122impl AccessoryCategory {
123    fn value(&self) -> u16 {
124        match *self {
125            AccessoryCategory::Other => 1,
126            AccessoryCategory::Bridge => 2,
127            AccessoryCategory::Fan => 3,
128            AccessoryCategory::Garage => 4,
129            AccessoryCategory::Lightbulb => 5,
130            AccessoryCategory::DoorLock => 6,
131            AccessoryCategory::Outlet => 7,
132            AccessoryCategory::Switch => 8,
133            AccessoryCategory::Thermostat => 9,
134            AccessoryCategory::Sensor => 10,
135            AccessoryCategory::SecuritySystem => 11,
136            AccessoryCategory::Door => 12,
137            AccessoryCategory::Window => 13,
138            AccessoryCategory::WindowCovering => 14,
139            AccessoryCategory::ProgrammableSwitch => 15,
140            AccessoryCategory::RangeExtender => 16,
141            AccessoryCategory::IPCamera => 17,
142            AccessoryCategory::VideoDoorBell => 18,
143            AccessoryCategory::AirPurifier => 19,
144            AccessoryCategory::Reserved => 65535,
145        }
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use insta::assert_debug_snapshot_matches;
153
154    #[test]
155    fn test_snapshots() {
156        assert_debug_snapshot_matches!(
157            "data_as_adstructure",
158            Data::new(
159                Interval::_501_1250MS,
160                PairingStatus::NotPaired,
161                188899839028173,
162                AccessoryCategory::Lightbulb,
163                1,
164                1,
165            )
166            .as_adstructure()
167        )
168    }
169}