Midea/src/discover.rs

205 lines
5.3 KiB
Rust
Raw Normal View History

2023-09-22 08:07:45 +00:00
use std::{
collections::HashMap,
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
2023-09-22 10:34:35 +00:00
str::from_utf8,
2023-09-22 08:07:45 +00:00
time::Duration,
};
use anyhow::Result;
2023-09-22 10:34:35 +00:00
use crate::{
hex,
security::{MsgType, Security},
};
#[derive(Debug, Clone)]
pub struct DeviceInfo {
id: u64,
model: String,
sn: String,
protocol: u8,
addr: SocketAddr,
}
pub struct Device {
info: DeviceInfo,
socket: UdpSocket,
2023-09-22 16:06:37 +00:00
security: Security,
2023-09-22 10:34:35 +00:00
}
impl Device {
pub fn connect(info: DeviceInfo) -> Result<Self> {
let socket = UdpSocket::bind("0.0.0.0:0")?;
socket.set_write_timeout(Some(Duration::from_secs(10)))?;
socket.set_read_timeout(Some(Duration::from_secs(10)))?;
socket.connect(info.addr)?;
2023-09-22 16:06:37 +00:00
let mut me = Self {
info,
socket,
security: Security::default(),
};
2023-09-22 10:34:35 +00:00
if me.info.protocol == 3 {
me.authenticate()?;
}
me.refresh_status()?;
Ok(me)
}
2023-09-22 16:06:37 +00:00
fn authenticate(&mut self) -> Result<()> {
let request = self.security.encode_8370(MsgType::HANDSHAKE_REQUEST)?;
2023-09-22 10:34:35 +00:00
Ok(())
}
pub fn refresh_status(&self) -> Result<()> {
//
Ok(())
}
}
struct Startup;
2023-09-22 08:07:45 +00:00
impl Startup {
const BROADCAST_MSG: &'static [u8] = &[
0x5a, 0x5a, 0x01, 0x11, 0x48, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x75, 0xbd, 0x6b, 0x3e,
0x4f, 0x8b, 0x76, 0x2e, 0x84, 0x9c, 0x6e, 0x57, 0x8d, 0x65, 0x90, 0x03, 0x6e, 0x9d, 0x43,
0x42, 0xa5, 0x0f, 0x1f, 0x56, 0x9e, 0xb8, 0xec, 0x91, 0x8e, 0x92, 0xe5,
];
const NUM_RETRIES: u8 = 5;
2023-09-22 10:34:35 +00:00
pub fn discover() -> Result<Vec<DeviceInfo>> {
2023-09-22 08:07:45 +00:00
let socket = UdpSocket::bind("0.0.0.0:0")?;
socket.set_broadcast(true)?;
socket.set_read_timeout(Some(Duration::from_secs(2)))?;
let mut devices = HashMap::new();
for addr in Self::broadcast_addresses() {
for _ in 0..Self::NUM_RETRIES {
socket.send_to(Self::BROADCAST_MSG, (addr, 6445))?;
socket.send_to(Self::BROADCAST_MSG, (addr, 20086))?;
let mut buffer = [0; 1024];
if let Ok((bytes_read, addr)) = socket.recv_from(&mut buffer) {
let mut bytes = buffer[0..bytes_read].to_vec();
2023-09-22 10:34:35 +00:00
let protocol = if bytes[..2] == hex("5a5a")? {
2023-09-22 08:07:45 +00:00
2
2023-09-22 10:34:35 +00:00
} else if bytes[..2] == hex("8370")? {
if bytes[8..10] == hex("5a5a")? {
2023-09-22 08:07:45 +00:00
bytes = bytes[8..(bytes.len() - 16)].to_vec();
}
3
} else {
continue;
};
let mut tmp: Vec<u8> = bytes[20..26].to_vec();
tmp.push(0);
tmp.push(0);
let device_id = u64::from_le_bytes(tmp.try_into().unwrap());
2023-09-22 10:34:35 +00:00
if devices.contains_key(&device_id) {
continue;
2023-09-22 08:07:45 +00:00
}
2023-09-22 10:34:35 +00:00
let len = bytes.len();
let encrypt_data = Security::decrypt(&mut bytes[40..(len - 16)]);
let model = from_utf8(&encrypt_data[17..25])?;
let sn = from_utf8(&encrypt_data[8..40])?;
devices.insert(
device_id,
(addr, model.to_string(), sn.to_string(), protocol),
);
2023-09-22 08:07:45 +00:00
}
}
}
2023-09-22 10:34:35 +00:00
Ok(devices
.into_iter()
.map(|(id, (addr, model, sn, protocol))| DeviceInfo {
id,
model,
sn,
protocol,
addr,
})
.collect())
2023-09-22 08:07:45 +00:00
}
}
impl Startup {
fn broadcast_addresses() -> Vec<IpAddr> {
use if_addrs::*;
let mut addresses = Vec::new();
if let Some(interfaces) = get_if_addrs().ok() {
for interface in interfaces {
match interface.addr {
IfAddr::V4(v4) => {
let ip = v4.ip;
let octets = ip.octets();
if ip.is_private() {
addresses.push(IpAddr::V4(Ipv4Addr::new(
octets[0],
octets[1],
octets[2],
u8::MAX,
)))
}
}
IfAddr::V6(_v6) => {
// TODO
// addresses.push(IpAddr::V6(v6.ip));
}
}
}
}
addresses
}
}
#[cfg(test)]
mod test {
use anyhow::Result;
2023-09-22 10:34:35 +00:00
use super::{Device, Startup};
2023-09-22 08:07:45 +00:00
#[test]
fn discover() -> Result<()> {
2023-09-22 10:34:35 +00:00
let devices = Startup::discover()?;
println!("{devices:#?}");
Ok(())
}
#[test]
fn connect() -> Result<()> {
for device_info in Startup::discover()? {
let device = Device::connect(device_info)?;
}
2023-09-22 08:07:45 +00:00
Ok(())
}
}