diff --git a/.vscode/settings.json b/.vscode/settings.json index ab9d94d..853ae20 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,9 @@ "activityBar.background": "#1C3310", "titleBar.activeBackground": "#274816", "titleBar.activeForeground": "#F6FBF4" - } + }, + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ], + "rust-analyzer.showUnlinkedFileNotification": false } \ No newline at end of file diff --git a/cloud.py b/cloud.py index 96db23d..5e39b1d 100644 --- a/cloud.py +++ b/cloud.py @@ -127,9 +127,6 @@ class MideaCloud: data.update({ "udpid": udp_id }) - - print(data) - response = await self._api_request( endpoint="/v1/iot/secure/getToken", data=data diff --git a/device.py b/device.py index 103f824..adff41f 100644 --- a/device.py +++ b/device.py @@ -73,9 +73,6 @@ class MiedaDevice(threading.Thread): self._heartbeat_interval = 10 self._default_refresh_interval = 30 - print(token) - print(self._token) - k = 0 @property @@ -151,24 +148,10 @@ class MiedaDevice(threading.Thread): request = self._security.encode_8370( self._token, MSGTYPE_HANDSHAKE_REQUEST) _LOGGER.debug(f"[{self._device_id}] Handshaking") - print("REQUEST") - print(request) - - req = bytearray([131, 112, 0, 64, 32, 0, 0, 0, 112, 43, 157, 252, 58, 198, 200, 41, 121, 152, 110, 227, 160, 83, 167, 111, 117, 249, 233, 199, 99, 206, 92, 37, 175, 92, 44, 201, 130, 247, 151, 169, 64, 154, 223, 243, 116, 94, 35, 254, 227, 164, 100, 215, 69, 224, 5, 200, 57, 239, 176, 184, 64, 130, 172, 201, 98, 229, 154, 184, 104, 62, 2, 153]) - - if (req == request): - print("both requests are the same") - - self._socket.send(req) - response = self._socket.recv(512) - - print(response) self._socket.send(request) response = self._socket.recv(512) - print(response) - if len(response) < 20: raise AuthException() response = response[8: 72] diff --git a/midea.py b/midea.py index 1b4f6f3..47c9b00 100644 --- a/midea.py +++ b/midea.py @@ -65,7 +65,7 @@ async def test(): attributes={} ) - if dev.connect(False): + if dev.connect(True): return dev dev = asyncio.run(test()) diff --git a/security.py b/security.py index 51b990f..1c62bb2 100644 --- a/security.py +++ b/security.py @@ -59,8 +59,6 @@ class CloudSecurity: @staticmethod def get_udp_id(appliance_id, method=0): - print() - if method == 0: bytes_id = bytes(reversed(appliance_id.to_bytes(8, "big"))) elif method == 1: @@ -70,21 +68,12 @@ class CloudSecurity: else: return None - print(bytes_id) - data = bytearray(sha256(bytes_id).digest()) - print(data) - for i in range(0, 16): data[i] ^= data[i + 16] - hex = data[0: 16].hex() - - print(hex) - print() - - return hex + return data[0: 16].hex() def set_aes_keys(self, key, iv): if isinstance(key, str): diff --git a/src/device.rs b/src/device.rs index ab964c8..13133ae 100644 --- a/src/device.rs +++ b/src/device.rs @@ -8,6 +8,7 @@ use std::{ use anyhow::{bail, Context, Error, Result}; use crate::{ + devices::{e1::E1, DeviceBackend}, hex, security::{MsgType, Security}, DeviceInfo, @@ -19,6 +20,8 @@ pub struct Device { socket: TcpStream, security: Security, + device_backend: Box, + token: [u8; 64], key: [u8; 32], } @@ -42,6 +45,12 @@ impl Device { socket.set_read_timeout(Some(Duration::from_secs(10)))?; let mut me = Self { + device_backend: Box::new(match info.device_type { + 0xE1 => E1::new()?, + + _ => bail!("unsupported device type: {:02X}", info.device_type), + }), + info, socket, security: Security::default(), @@ -82,8 +91,6 @@ impl Device { } pub fn refresh_status(&self) -> Result<()> { - // - Ok(()) } } diff --git a/src/devices/e1.rs b/src/devices/e1.rs new file mode 100644 index 0000000..b0ded97 --- /dev/null +++ b/src/devices/e1.rs @@ -0,0 +1,209 @@ +use std::collections::HashMap; + +use anyhow::Result; + +use super::DeviceBackend; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum DeviceAttributes { + Power, + Status, + Mode, + Additional, + Door, + RinseAid, + Salt, + ChildLock, + UV, + Dry, + DryStatus, + Storage, + StorageStatus, + TimeRemaining, + Progress, + StorageRemaining, + Temperature, + Humidity, + Waterswitch, + WaterLack, + ErrorCode, + Softwater, + WrongOperation, + Bright, +} + +impl DeviceAttributes { + fn as_str(&self) -> &str { + match self { + DeviceAttributes::Power => "power", + DeviceAttributes::Status => "status", + DeviceAttributes::Mode => "mode", + DeviceAttributes::Additional => "additional", + DeviceAttributes::Door => "door", + DeviceAttributes::RinseAid => "rinse_aid", + DeviceAttributes::Salt => "salt", + DeviceAttributes::ChildLock => "child_lock", + DeviceAttributes::UV => "uv", + DeviceAttributes::Dry => "dry", + DeviceAttributes::DryStatus => "dry_status", + DeviceAttributes::Storage => "storage", + DeviceAttributes::StorageStatus => "storage_status", + DeviceAttributes::TimeRemaining => "time_remaining", + DeviceAttributes::Progress => "progress", + DeviceAttributes::StorageRemaining => "storage_remaining", + DeviceAttributes::Temperature => "temperature", + DeviceAttributes::Humidity => "humidity", + DeviceAttributes::Waterswitch => "waterswitch", + DeviceAttributes::WaterLack => "water_lack", + DeviceAttributes::ErrorCode => "error_code", + DeviceAttributes::Softwater => "softwater", + DeviceAttributes::WrongOperation => "wrong_operation", + DeviceAttributes::Bright => "bright", + } + } + + fn from_str(s: &str) -> Self { + match s { + "power" => Self::Power, + "status" => Self::Status, + "mode" => Self::Mode, + "additional" => Self::Additional, + "door" => Self::Door, + "rinse_aid" => Self::RinseAid, + "salt" => Self::Salt, + "child_lock" => Self::ChildLock, + "uv" => Self::UV, + "dry" => Self::Dry, + "dry_status" => Self::DryStatus, + "storage" => Self::Storage, + "storage_status" => Self::StorageStatus, + "time_remaining" => Self::TimeRemaining, + "progress" => Self::Progress, + "storage_remaining" => Self::StorageRemaining, + "temperature" => Self::Temperature, + "humidity" => Self::Humidity, + "waterswitch" => Self::Waterswitch, + "water_lack" => Self::WaterLack, + "error_code" => Self::ErrorCode, + "softwater" => Self::Softwater, + "wrong_operation" => Self::WrongOperation, + "bright" => Self::Bright, + + _ => panic!(), + } + } +} + +enum AttributeValue { + String(Option), + Bool(bool), + Int(i32), +} + +pub struct E1 { + modes: HashMap, + attributes: HashMap, + + status: [&'static str; 5], + progress: [&'static str; 6], +} + +impl E1 { + pub fn new() -> Result { + let modes = [ + (0x0, "Neutral Gear"), + (0x1, "Auto"), + (0x2, "Heavy"), + (0x3, "Normal"), + (0x4, "Energy Saving"), + (0x5, "Delicate"), + (0x6, "Hour"), + (0x7, "Quick"), + (0x8, "Rinse"), + (0x9, "90min"), + (0xA, "Self Clean"), + (0xB, "Fruit Wash"), + (0xC, "Self Define"), + (0xD, "Germ"), + (0xE, "Bowl Wash"), + (0xF, "Kill Germ"), + (0x10, "Sea Food Wash"), + (0x12, "Hot Pot Wash"), + (0x13, "Quiet"), + (0x14, "Less Wash"), + (0x16, "Oil Net Wash"), + (0x19, "Cloud Wash"), + ] + .into_iter() + .map(|(i, s)| (i, s.to_string())) + .collect(); + + let attributes = [ + (DeviceAttributes::Power, AttributeValue::Bool(false)), + (DeviceAttributes::Status, AttributeValue::String(None)), + (DeviceAttributes::Mode, AttributeValue::Int(0)), + (DeviceAttributes::Additional, AttributeValue::Int(0)), + (DeviceAttributes::UV, AttributeValue::Bool(false)), + (DeviceAttributes::Dry, AttributeValue::Bool(false)), + (DeviceAttributes::DryStatus, AttributeValue::Bool(false)), + (DeviceAttributes::Door, AttributeValue::Bool(false)), + (DeviceAttributes::RinseAid, AttributeValue::Bool(false)), + (DeviceAttributes::Salt, AttributeValue::Bool(false)), + (DeviceAttributes::ChildLock, AttributeValue::Bool(false)), + (DeviceAttributes::Storage, AttributeValue::Bool(false)), + (DeviceAttributes::StorageStatus, AttributeValue::Bool(false)), + ( + DeviceAttributes::TimeRemaining, + AttributeValue::String(None), + ), + (DeviceAttributes::Progress, AttributeValue::String(None)), + ( + DeviceAttributes::StorageRemaining, + AttributeValue::String(None), + ), + (DeviceAttributes::Temperature, AttributeValue::String(None)), + (DeviceAttributes::Humidity, AttributeValue::String(None)), + (DeviceAttributes::Waterswitch, AttributeValue::Bool(false)), + (DeviceAttributes::WaterLack, AttributeValue::Bool(false)), + (DeviceAttributes::ErrorCode, AttributeValue::String(None)), + (DeviceAttributes::Softwater, AttributeValue::Int(0)), + ( + DeviceAttributes::WrongOperation, + AttributeValue::String(None), + ), + (DeviceAttributes::Bright, AttributeValue::Int(0)), + ] + .into_iter() + .collect(); + + let status = ["Off", "Idle", "Delay", "Running", "Error"]; + let progress = ["Idle", "Pre-wash", "Wash", "Rinse", "Dry", "Complete"]; + + Ok(Self { + modes, + attributes, + status, + progress, + }) + } +} + +impl DeviceBackend for E1 { + fn build_query(&self) -> () { + todo!() + } + + fn process_message(&self, msg: &str) -> () { + todo!() + } + + fn set_attribute(&self, attribute: &str, value: &str) -> () { + match DeviceAttributes::from_str(attribute) { + DeviceAttributes::Power => (), + DeviceAttributes::ChildLock => (), + DeviceAttributes::Storage => (), + + _ => (), + } + } +} diff --git a/src/devices/mod.rs b/src/devices/mod.rs new file mode 100644 index 0000000..4cd1576 --- /dev/null +++ b/src/devices/mod.rs @@ -0,0 +1,7 @@ +pub mod e1; + +pub trait DeviceBackend { + fn build_query(&self) -> (); + fn process_message(&self, msg: &str) -> (); + fn set_attribute(&self, attribute: &str, value: &str) -> (); +} diff --git a/src/lib.rs b/src/lib.rs index 78ed6be..f6e96d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::num::ParseIntError; mod cloud; mod cloud_security; mod device; +mod devices; mod discover; mod security; diff --git a/src/security.rs b/src/security.rs index 8a3416b..6173b71 100644 --- a/src/security.rs +++ b/src/security.rs @@ -16,7 +16,6 @@ use crate::hex; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum MsgType { HANDSHAKE_REQUEST = 0x0, - HANDSHAKE_RESPONSE = 0x1, ENCRYPTED_RESPONSE = 0x3, ENCRYPTED_REQUEST = 0x6, }