Skip to main content

rs_matter/pairing/
code.rs

1/*
2 *
3 *    Copyright (c) 2023-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use core::fmt::Write;
19
20use verhoeff::Verhoeff;
21
22use crate::BasicCommData;
23
24impl BasicCommData {
25    /// Compute the manual pairing code string from the commissioning data
26    pub fn compute_pairing_code(&self) -> heapless::String<11> {
27        // 0: no Vendor ID and Product ID present in Manual Pairing Code
28        const VID_PID_PRESENT: u8 = 0;
29
30        let BasicCommData {
31            password,
32            discriminator,
33            ..
34        } = self;
35
36        let password = u32::from_le_bytes(*password.access());
37        let mut digits = heapless::String::<10>::new();
38        write_unwrap!(
39            &mut digits,
40            "{}{:0>5}{:0>4}",
41            (VID_PID_PRESENT << 2) | (discriminator >> 10) as u8,
42            ((discriminator & 0x300) << 6) | (password & 0x3FFF) as u16,
43            password >> 14
44        );
45
46        let mut final_digits = heapless::String::<11>::new();
47        write_unwrap!(
48            &mut final_digits,
49            "{}{}",
50            digits,
51            digits.calculate_verhoeff_check_digit()
52        );
53
54        final_digits
55    }
56
57    /// Compute the manual pairing code string from the commissioning data
58    /// and return it in a pretty format with dashes.
59    pub fn compute_pretty_pairing_code(&self) -> heapless::String<13> {
60        let pairing_code = self.compute_pairing_code();
61
62        let mut pretty = heapless::String::new();
63        unwrap!(pretty.push_str(&pairing_code[..4]));
64        unwrap!(pretty.push('-'));
65        unwrap!(pretty.push_str(&pairing_code[4..8]));
66        unwrap!(pretty.push('-'));
67        unwrap!(pretty.push_str(&pairing_code[8..]));
68
69        pretty
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn can_compute_pairing_code() {
79        let comm_data = BasicCommData {
80            password: 123456_u32.to_le_bytes().into(),
81            discriminator: 250,
82        };
83        let pairing_code = comm_data.compute_pairing_code();
84        assert_eq!(pairing_code, "00876800071");
85
86        let comm_data = BasicCommData {
87            password: 34567890_u32.to_le_bytes().into(),
88            discriminator: 2976,
89        };
90        let pairing_code = comm_data.compute_pairing_code();
91        assert_eq!(pairing_code, "26318621095");
92    }
93}