Start E1 device
This commit is contained in:
parent
d3fb193e8d
commit
6f1106ee28
10 changed files with 233 additions and 37 deletions
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -3,5 +3,9 @@
|
||||||
"activityBar.background": "#1C3310",
|
"activityBar.background": "#1C3310",
|
||||||
"titleBar.activeBackground": "#274816",
|
"titleBar.activeBackground": "#274816",
|
||||||
"titleBar.activeForeground": "#F6FBF4"
|
"titleBar.activeForeground": "#F6FBF4"
|
||||||
}
|
},
|
||||||
|
"rust-analyzer.linkedProjects": [
|
||||||
|
"./Cargo.toml"
|
||||||
|
],
|
||||||
|
"rust-analyzer.showUnlinkedFileNotification": false
|
||||||
}
|
}
|
3
cloud.py
3
cloud.py
|
@ -127,9 +127,6 @@ class MideaCloud:
|
||||||
data.update({
|
data.update({
|
||||||
"udpid": udp_id
|
"udpid": udp_id
|
||||||
})
|
})
|
||||||
|
|
||||||
print(data)
|
|
||||||
|
|
||||||
response = await self._api_request(
|
response = await self._api_request(
|
||||||
endpoint="/v1/iot/secure/getToken",
|
endpoint="/v1/iot/secure/getToken",
|
||||||
data=data
|
data=data
|
||||||
|
|
17
device.py
17
device.py
|
@ -73,9 +73,6 @@ class MiedaDevice(threading.Thread):
|
||||||
self._heartbeat_interval = 10
|
self._heartbeat_interval = 10
|
||||||
self._default_refresh_interval = 30
|
self._default_refresh_interval = 30
|
||||||
|
|
||||||
print(token)
|
|
||||||
print(self._token)
|
|
||||||
|
|
||||||
k = 0
|
k = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -151,24 +148,10 @@ class MiedaDevice(threading.Thread):
|
||||||
request = self._security.encode_8370(
|
request = self._security.encode_8370(
|
||||||
self._token, MSGTYPE_HANDSHAKE_REQUEST)
|
self._token, MSGTYPE_HANDSHAKE_REQUEST)
|
||||||
_LOGGER.debug(f"[{self._device_id}] Handshaking")
|
_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)
|
self._socket.send(request)
|
||||||
response = self._socket.recv(512)
|
response = self._socket.recv(512)
|
||||||
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
if len(response) < 20:
|
if len(response) < 20:
|
||||||
raise AuthException()
|
raise AuthException()
|
||||||
response = response[8: 72]
|
response = response[8: 72]
|
||||||
|
|
2
midea.py
2
midea.py
|
@ -65,7 +65,7 @@ async def test():
|
||||||
attributes={}
|
attributes={}
|
||||||
)
|
)
|
||||||
|
|
||||||
if dev.connect(False):
|
if dev.connect(True):
|
||||||
return dev
|
return dev
|
||||||
|
|
||||||
dev = asyncio.run(test())
|
dev = asyncio.run(test())
|
||||||
|
|
13
security.py
13
security.py
|
@ -59,8 +59,6 @@ class CloudSecurity:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_udp_id(appliance_id, method=0):
|
def get_udp_id(appliance_id, method=0):
|
||||||
print()
|
|
||||||
|
|
||||||
if method == 0:
|
if method == 0:
|
||||||
bytes_id = bytes(reversed(appliance_id.to_bytes(8, "big")))
|
bytes_id = bytes(reversed(appliance_id.to_bytes(8, "big")))
|
||||||
elif method == 1:
|
elif method == 1:
|
||||||
|
@ -70,21 +68,12 @@ class CloudSecurity:
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
print(bytes_id)
|
|
||||||
|
|
||||||
data = bytearray(sha256(bytes_id).digest())
|
data = bytearray(sha256(bytes_id).digest())
|
||||||
|
|
||||||
print(data)
|
|
||||||
|
|
||||||
for i in range(0, 16):
|
for i in range(0, 16):
|
||||||
data[i] ^= data[i + 16]
|
data[i] ^= data[i + 16]
|
||||||
|
|
||||||
hex = data[0: 16].hex()
|
return data[0: 16].hex()
|
||||||
|
|
||||||
print(hex)
|
|
||||||
print()
|
|
||||||
|
|
||||||
return hex
|
|
||||||
|
|
||||||
def set_aes_keys(self, key, iv):
|
def set_aes_keys(self, key, iv):
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
use anyhow::{bail, Context, Error, Result};
|
use anyhow::{bail, Context, Error, Result};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
devices::{e1::E1, DeviceBackend},
|
||||||
hex,
|
hex,
|
||||||
security::{MsgType, Security},
|
security::{MsgType, Security},
|
||||||
DeviceInfo,
|
DeviceInfo,
|
||||||
|
@ -19,6 +20,8 @@ pub struct Device {
|
||||||
socket: TcpStream,
|
socket: TcpStream,
|
||||||
security: Security,
|
security: Security,
|
||||||
|
|
||||||
|
device_backend: Box<dyn DeviceBackend>,
|
||||||
|
|
||||||
token: [u8; 64],
|
token: [u8; 64],
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
}
|
}
|
||||||
|
@ -42,6 +45,12 @@ impl Device {
|
||||||
socket.set_read_timeout(Some(Duration::from_secs(10)))?;
|
socket.set_read_timeout(Some(Duration::from_secs(10)))?;
|
||||||
|
|
||||||
let mut me = Self {
|
let mut me = Self {
|
||||||
|
device_backend: Box::new(match info.device_type {
|
||||||
|
0xE1 => E1::new()?,
|
||||||
|
|
||||||
|
_ => bail!("unsupported device type: {:02X}", info.device_type),
|
||||||
|
}),
|
||||||
|
|
||||||
info,
|
info,
|
||||||
socket,
|
socket,
|
||||||
security: Security::default(),
|
security: Security::default(),
|
||||||
|
@ -82,8 +91,6 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_status(&self) -> Result<()> {
|
pub fn refresh_status(&self) -> Result<()> {
|
||||||
//
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
209
src/devices/e1.rs
Normal file
209
src/devices/e1.rs
Normal file
|
@ -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<String>),
|
||||||
|
Bool(bool),
|
||||||
|
Int(i32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct E1 {
|
||||||
|
modes: HashMap<u32, String>,
|
||||||
|
attributes: HashMap<DeviceAttributes, AttributeValue>,
|
||||||
|
|
||||||
|
status: [&'static str; 5],
|
||||||
|
progress: [&'static str; 6],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl E1 {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
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 => (),
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/devices/mod.rs
Normal file
7
src/devices/mod.rs
Normal file
|
@ -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) -> ();
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ use std::num::ParseIntError;
|
||||||
mod cloud;
|
mod cloud;
|
||||||
mod cloud_security;
|
mod cloud_security;
|
||||||
mod device;
|
mod device;
|
||||||
|
mod devices;
|
||||||
mod discover;
|
mod discover;
|
||||||
mod security;
|
mod security;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ use crate::hex;
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||||
pub enum MsgType {
|
pub enum MsgType {
|
||||||
HANDSHAKE_REQUEST = 0x0,
|
HANDSHAKE_REQUEST = 0x0,
|
||||||
HANDSHAKE_RESPONSE = 0x1,
|
|
||||||
ENCRYPTED_RESPONSE = 0x3,
|
ENCRYPTED_RESPONSE = 0x3,
|
||||||
ENCRYPTED_REQUEST = 0x6,
|
ENCRYPTED_REQUEST = 0x6,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue