gree/
state.rs

1use std::{time::Duration, collections::HashMap, net::{IpAddr, SocketAddr, Ipv4Addr}};
2
3use serde_json::Value;
4
5use crate::{*, apdu::{ScanResponsePack, GenericMessage, BindResponsePack}, vars::VarName};
6
7pub type MacAddr = String;
8
9/// Low-level Gree client configuration
10#[derive(Debug, Clone, Copy)]
11pub struct GreeClientConfig {
12    /// Recv datagram buffer size
13    pub buffer_size: usize,
14    /// Socket recv timeout
15    pub recv_timeout: Duration,
16    /// Socket addr to bind to
17    pub bind_addr: SocketAddr,
18    /// Maximum devices to be discovered diring a scan. The scan is stopped early when this number of devices is reached.
19    pub max_count: usize,
20    /// Broadcast address for the network.
21    pub bcast_addr: IpAddr,
22}
23
24impl GreeClientConfig {
25    pub const DEFAULT_BUFFER_SIZE: usize = 2048;
26    pub const DEFAULT_MAX_COUNT: usize = 10;
27    pub const DEFAULT_BROADCAST_ADDR: [u8; 4] =  [10, 0, 0, 255];
28    pub const DEFAULT_RECV_TIMEOUT: Duration = Duration::from_secs(3);
29}
30
31impl Default for GreeClientConfig {
32
33    fn default() -> Self {
34        Self {
35            buffer_size: Self::DEFAULT_BUFFER_SIZE,
36            recv_timeout: Self::DEFAULT_RECV_TIMEOUT,
37            bind_addr: (Ipv4Addr::UNSPECIFIED, 0).into(),
38            max_count: Self::DEFAULT_MAX_COUNT, 
39            bcast_addr: Self::DEFAULT_BROADCAST_ADDR.into(), 
40        }
41    }
42}
43
44/// Gree network configuration
45#[derive(Debug, Clone)]
46pub struct GreeConfig {
47    /// lower level client configuration
48    pub client_config: GreeClientConfig,
49    /// Minimum scan age. Scan is always bypassed if the last successful scan is younger than this value. 
50    pub min_scan_age: Duration,
51    /// Maximum scan age. Scan is forced if the last (successful) scan is older than this value.
52    pub max_scan_age: Duration,
53    /// Aliases for the network devices
54    pub aliases: HashMap<String, MacAddr>,
55}
56
57impl GreeConfig {
58
59    pub const DEFAULT_MIN_SCAN_AGE: Duration = Duration::from_secs(60);
60    pub const DEFAULT_MAX_SCAN_AGE: Duration = Duration::from_secs(3600 * 24);
61}
62
63impl Default for GreeConfig {
64    fn default() -> Self {
65        Self {
66            client_config: Default::default(),
67            min_scan_age: Self::DEFAULT_MIN_SCAN_AGE, 
68            max_scan_age: Self::DEFAULT_MAX_SCAN_AGE,
69            aliases: HashMap::new(),
70        }
71    }
72}
73
74/// State of Gree network
75pub struct GreeState {
76    pub devices: HashMap<MacAddr, Device>,
77}
78
79impl GreeState {
80    pub fn new() -> Self { Self { devices: HashMap::new() } }
81    pub fn scan_ind(&mut self, scan_result: Vec<(IpAddr, GenericMessage, ScanResponsePack)>) {
82        self.devices = scan_result.into_iter().map(|(ip, _, scan_result)| (
83            scan_result.mac.clone(),
84            Device { ip, scan_result, key: None }
85        )).collect();
86    }
87}
88
89/// Information about a gree Device on the network.
90/// 
91/// Devices are typically discovered during scans. The `key` field is set as a result of successful binding.
92pub struct Device {
93    /// Known IP address of the device. 
94    pub ip: IpAddr,
95
96    /// Device's scan respobse
97    pub scan_result: ScanResponsePack,
98
99    /// Encryption key (if bound)
100    pub key: Option<String>,
101}
102
103impl Device {
104    pub fn bind_ind(&mut self, pack: BindResponsePack) {
105        self.key = Some(pack.key)
106    }
107}
108
109
110/// Network Variable (NetVar) defines a protocol for exchanging Values with the network.
111/// 
112/// It may be considered a placeholder for a Value that can be read from or written to the network.
113pub trait NetVar {
114    /// Stores the value received from the network and clears net_read_pending
115    fn net_set(&mut self, value: Value);
116    /// Returns the value to be written to the network
117    fn net_get(&self) -> &Value;
118    /// True if the value of this NetVar is supposed to be read and set from the network
119    fn is_net_read_pending(&self) -> bool;
120    /// True if the value of this NetVar is supposed to be written to the network
121    fn is_net_write_pending(&self) -> bool;
122    /// Signal that the value of this NetVar doesn't need to be written to the network anymore (typically after a successful net write)
123    fn clear_net_write_pending(&mut self);
124}
125
126
127/// A basic implementation of [NetVar]
128pub struct SimpleNetVar {
129    value: Value,
130    net_read_pending: bool,
131    net_write_pending: bool,
132}
133
134impl SimpleNetVar {
135    pub fn new() -> Self {
136        Self { value: Value::Null, net_read_pending: true, net_write_pending: false }
137    }
138
139    /// Parses variable setting and adds it to a `NetVarBag`. The `NetVarBag` might then be used for a `net_write`.
140    pub fn add_nv_to(mut bag: NetVarBag<Self>, (name, value): (impl AsRef<str>, impl AsRef<str>)) -> Result<NetVarBag<Self>> {
141        let name = vars::name_of(name.as_ref())
142            .ok_or_else(|| Error::InvalidVar(name.as_ref().to_owned()))?;
143        let value = vars::parse_value(name, value)?;
144        bag.insert(name, Self::from_value(value));
145        Ok(bag)
146    }
147
148    /// Parses variable name and adds it to a `NetVarBag`. The `NetVarBag` might then be used for a `net_read`.
149    pub fn add_n_to(mut bag: NetVarBag<Self>, name: impl AsRef<str>) -> Result<NetVarBag<Self>> {
150        let name = vars::name_of(name.as_ref())
151            .ok_or_else(|| Error::InvalidVar(name.as_ref().to_owned()))?;
152        bag.insert(name, Self::new());
153        Ok(bag)
154    }
155
156    /// Creates a `SimpleNetVar` from a value. The `SimpleNetVar` might then be used for a `net_write`. 
157    pub fn from_value(value: Value) -> Self {
158        Self { value, net_read_pending: false, net_write_pending: true }
159    }
160
161    /// Sets a value of the `SimpleNetVar` from the user side. The `SimpleNetVar` might then be used for a `net_write`. 
162    pub fn user_set(&mut self, value: Value) {
163        self.value = value;
164        self.net_write_pending = true;
165    }
166
167    /// Gets a value of the `SimpleNetVar` from the user side, typically after a `net_read`.
168    pub fn user_get(&self) -> &Value {
169        &self.value
170    }
171}
172
173impl NetVar for SimpleNetVar {
174    //fn get_name(&self) -> &'static str { self.name }
175    fn net_set(&mut self, value: Value) { 
176        self.value = value;
177        self.net_read_pending = false;
178    }
179    fn net_get(&self) -> &Value { &self.value }
180    fn is_net_read_pending(&self) -> bool { self.net_read_pending }
181    fn is_net_write_pending(&self) -> bool { self.net_write_pending }
182    fn clear_net_write_pending(&mut self) { self.net_write_pending = false }
183}
184
185/// A collection of network variables by internalized name
186pub type NetVarBag<T> = HashMap<VarName, T>;
187
188/// Constructs NetVarBag from an iterator of names. The bag returned is ready to be used in a network read call.
189pub fn net_var_bag_from_names<'t, S: AsRef<str> + 't>(mut ns: impl Iterator<Item = &'t S>) -> Result<NetVarBag<SimpleNetVar>> {
190    ns.try_fold(std::collections::HashMap::new(), SimpleNetVar::add_n_to)
191}
192
193/// Constructs NetVarBag from an iterator of (name, value) pairs. The bag returned is ready to be used in a network write call.
194pub fn net_var_bag_from_nvs<'t, S: AsRef<str> + 't>(mut nvs: impl Iterator<Item = (&'t S, &'t S)>) -> Result<NetVarBag<SimpleNetVar>> {
195    nvs.try_fold(std::collections::HashMap::new(), SimpleNetVar::add_nv_to)
196}
197
198/// Converts NetVarBag into a json. Convenient for value reporting.
199pub fn net_var_bag_to_json<T: NetVar>(b: &NetVarBag<T>) -> HashMap<VarName, Value> {
200    b.into_iter().map(|(k, v)| (*k, v.net_get().clone())).collect()
201}
202
203/// Constructs NetVarBag of [SimpleNetVar]s, for reading (from keys) or writing (from key => value pairs)
204#[macro_export]
205macro_rules! net_var_bag {
206    ($($var:expr => $val:expr),+) => {
207        [$(($var, $val)),+].into_iter().try_fold(std::collections::HashMap::new(), gree::SimpleNetVar::add_nv_to)
208    };
209    ($($var:expr),+) => {
210        [$($var),+].into_iter().try_fold(std::collections::HashMap::new(), gree::SimpleNetVar::add_n_to)
211    };
212}
213
214/// NetVar Operation
215#[derive(Debug)]
216pub enum Op<'t, T: NetVar> {
217    Bind,
218    NetRead(&'t mut NetVarBag<T>),
219    NetWrite(&'t mut NetVarBag<T>),
220}