1use std::{fmt, str::FromStr};
2
3#[derive(Debug, Clone)]
4pub struct Credentials {
5 pub app_eui: AppEui,
6 pub app_key: AppKey,
7 pub dev_eui: DevEui,
8}
9
10impl Credentials {
11 pub fn new(dev_eui: DevEui, app_eui: AppEui, app_key: AppKey) -> Self {
12 Self {
13 dev_eui,
14 app_eui,
15 app_key,
16 }
17 }
18}
19
20macro_rules! derive_from_str {
21 ($name:ident, $size:expr) => {
22 #[derive(Debug, Clone, PartialEq, Eq)]
23 pub struct $name([u8; $size]);
24
25 impl FromStr for $name {
26 type Err = ParseError;
27
28 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
29 let mut s = s.to_string();
30 s.retain(|c| c != ':');
31 let byte_vec = hex::decode(&s)?;
32 let len = byte_vec.len();
33 let byte_arr: [u8; $size] = byte_vec
34 .try_into()
35 .map_err(|_| ParseError::VecWrongSize(len))?;
36 Ok(Self(byte_arr))
37 }
38 }
39
40 impl fmt::Display for $name {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 let str = hex::encode(&self.0).to_uppercase();
43 write!(f, "{str}")
44 }
45 }
46
47 impl From<[u8; $size]> for $name {
48 fn from(arr: [u8; $size]) -> Self {
49 Self(arr)
50 }
51 }
52 };
53}
54
55derive_from_str!(AppEui, 8);
56derive_from_str!(DevEui, 8);
57derive_from_str!(AppKey, 16);
58
59use super::*;
60
61impl<const N: usize> LoraE5<N> {
62 pub fn get_dev_eui(&mut self) -> Result<DevEui> {
63 const EXPECTED_PRELUDE: &str = "+ID: DevEui, ";
64 self.write_command("AT+ID=DevEui")?;
65 let n = self.read_until_break(DEFAULT_TIMEOUT)?;
66 let response = self.framed_response(n, EXPECTED_PRELUDE)?;
67 Ok(DevEui::from_str(response.trim_end())?)
68 }
69
70 pub fn get_app_eui(&mut self) -> Result<AppEui> {
71 const EXPECTED_PRELUDE: &str = "+ID: AppEui, ";
72 self.write_command("AT+ID=AppEui")?;
73 let n = self.read_until_break(DEFAULT_TIMEOUT)?;
74 let response = self.framed_response(n, EXPECTED_PRELUDE)?;
75 Ok(AppEui::from_str(response.trim_end())?)
76 }
77
78 pub fn set_app_eui(&mut self, app_eui: &AppEui) -> Result {
79 const EXPECTED_PRELUDE: &str = "+ID: AppEui, ";
80 let cmd = format!("AT+ID=AppEui, {app_eui}");
81 self.write_command(&cmd)?;
82 let n = self.read_until_break(DEFAULT_TIMEOUT)?;
83 let response = self.framed_response(n, EXPECTED_PRELUDE)?;
84 let app_eui_response = AppEui::from_str(response.trim_end())?;
85 if &app_eui_response == app_eui {
86 Ok(())
87 } else {
88 Err(Error::UnexpectedResponse(app_eui_response.to_string()))
89 }
90 }
91
92 pub fn set_dev_eui(&mut self, dev_eui: &DevEui) -> Result {
93 const EXPECTED_PRELUDE: &str = "+ID: DevEui, ";
94 let cmd = format!("AT+ID=DevEui, {dev_eui}");
95 self.write_command(&cmd)?;
96 let n = self.read_until_break(DEFAULT_TIMEOUT)?;
97 let response = self.framed_response(n, EXPECTED_PRELUDE)?;
98 let dev_eui_response = DevEui::from_str(response.trim_end())?;
99 if &dev_eui_response == dev_eui {
100 Ok(())
101 } else {
102 Err(Error::UnexpectedResponse(dev_eui_response.to_string()))
103 }
104 }
105
106 pub fn set_app_key(&mut self, app_key: &AppKey) -> Result {
107 const EXPECTED_PRELUDE: &str = "+KEY: APPKEY ";
108 let cmd = format!("AT+KEY=APPKEY, {app_key}");
109 self.write_command(&cmd)?;
110 let n = self.read_until_break(DEFAULT_TIMEOUT)?;
111 let response = self.framed_response(n, EXPECTED_PRELUDE)?;
112 let app_key_response = AppKey::from_str(response.trim_end())?;
113 if &app_key_response == app_key {
114 Ok(())
115 } else {
116 Err(Error::UnexpectedResponse(response.to_string()))
117 }
118 }
119
120 pub fn set_credentials(&mut self, credentials: &Credentials) -> Result {
121 self.set_dev_eui(&credentials.dev_eui)?;
122 self.set_app_eui(&credentials.app_eui)?;
123 self.set_app_key(&credentials.app_key)
124 }
125}
126
127use thiserror::Error;
128
129#[derive(Error, Debug)]
130pub enum ParseError {
131 #[error("hex error: {0}")]
132 FromHex(#[from] hex::FromHexError),
133 #[error("Vec is unexpected of len {0}")]
134 VecWrongSize(usize),
135}