1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::{fmt, str::FromStr};

#[derive(Debug, Clone)]
pub struct Credentials {
    pub app_eui: AppEui,
    pub app_key: AppKey,
    pub dev_eui: DevEui,
}

impl Credentials {
    pub fn new(dev_eui: DevEui, app_eui: AppEui, app_key: AppKey) -> Self {
        Self {
            dev_eui,
            app_eui,
            app_key,
        }
    }
}

macro_rules! derive_from_str {
    ($name:ident, $size:expr) => {
        #[derive(Debug, Clone, PartialEq, Eq)]
        pub struct $name([u8; $size]);

        impl FromStr for $name {
            type Err = ParseError;

            fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
                let mut s = s.to_string();
                s.retain(|c| c != ':');
                let byte_vec = hex::decode(&s)?;
                let len = byte_vec.len();
                let byte_arr: [u8; $size] = byte_vec
                    .try_into()
                    .map_err(|_| ParseError::VecWrongSize(len))?;
                Ok(Self(byte_arr))
            }
        }

        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                let str = hex::encode(&self.0).to_uppercase();
                write!(f, "{str}")
            }
        }

        impl From<[u8; $size]> for $name {
            fn from(arr: [u8; $size]) -> Self {
                Self(arr)
            }
        }
    };
}

derive_from_str!(AppEui, 8);
derive_from_str!(DevEui, 8);
derive_from_str!(AppKey, 16);

use super::*;

impl<const N: usize> LoraE5<N> {
    pub fn get_dev_eui(&mut self) -> Result<DevEui> {
        const EXPECTED_PRELUDE: &str = "+ID: DevEui, ";
        self.write_command("AT+ID=DevEui")?;
        let n = self.read_until_break(DEFAULT_TIMEOUT)?;
        let response = self.framed_response(n, EXPECTED_PRELUDE)?;
        Ok(DevEui::from_str(response.trim_end())?)
    }

    pub fn get_app_eui(&mut self) -> Result<AppEui> {
        const EXPECTED_PRELUDE: &str = "+ID: AppEui, ";
        self.write_command("AT+ID=AppEui")?;
        let n = self.read_until_break(DEFAULT_TIMEOUT)?;
        let response = self.framed_response(n, EXPECTED_PRELUDE)?;
        Ok(AppEui::from_str(response.trim_end())?)
    }

    pub fn set_app_eui(&mut self, app_eui: &AppEui) -> Result {
        const EXPECTED_PRELUDE: &str = "+ID: AppEui, ";
        let cmd = format!("AT+ID=AppEui, {app_eui}");
        self.write_command(&cmd)?;
        let n = self.read_until_break(DEFAULT_TIMEOUT)?;
        let response = self.framed_response(n, EXPECTED_PRELUDE)?;
        let app_eui_response = AppEui::from_str(response.trim_end())?;
        if &app_eui_response == app_eui {
            Ok(())
        } else {
            Err(Error::UnexpectedResponse(app_eui_response.to_string()))
        }
    }

    pub fn set_dev_eui(&mut self, dev_eui: &DevEui) -> Result {
        const EXPECTED_PRELUDE: &str = "+ID: DevEui, ";
        let cmd = format!("AT+ID=DevEui, {dev_eui}");
        self.write_command(&cmd)?;
        let n = self.read_until_break(DEFAULT_TIMEOUT)?;
        let response = self.framed_response(n, EXPECTED_PRELUDE)?;
        let dev_eui_response = DevEui::from_str(response.trim_end())?;
        if &dev_eui_response == dev_eui {
            Ok(())
        } else {
            Err(Error::UnexpectedResponse(dev_eui_response.to_string()))
        }
    }

    pub fn set_app_key(&mut self, app_key: &AppKey) -> Result {
        const EXPECTED_PRELUDE: &str = "+KEY: APPKEY ";
        let cmd = format!("AT+KEY=APPKEY, {app_key}");
        self.write_command(&cmd)?;
        let n = self.read_until_break(DEFAULT_TIMEOUT)?;
        let response = self.framed_response(n, EXPECTED_PRELUDE)?;
        let app_key_response = AppKey::from_str(response.trim_end())?;
        if &app_key_response == app_key {
            Ok(())
        } else {
            Err(Error::UnexpectedResponse(response.to_string()))
        }
    }

    pub fn set_credentials(&mut self, credentials: &Credentials) -> Result {
        self.set_dev_eui(&credentials.dev_eui)?;
        self.set_app_eui(&credentials.app_eui)?;
        self.set_app_key(&credentials.app_key)
    }
}

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ParseError {
    #[error("hex error: {0}")]
    FromHex(#[from] hex::FromHexError),
    #[error("Vec is unexpected of len {0}")]
    VecWrongSize(usize),
}