Start implementing write
This commit is contained in:
parent
1268d2df99
commit
2ecd31e9fe
3 changed files with 267 additions and 88 deletions
|
@ -45,7 +45,7 @@ pub struct Device {
|
|||
}
|
||||
|
||||
impl Device {
|
||||
pub fn connect(info: DeviceInfo, token: &str, key: &str) -> Result<Self> {
|
||||
pub async fn connect(info: DeviceInfo, token: &str, key: &str) -> Result<Self> {
|
||||
let mut socket = Err(Error::msg(""));
|
||||
|
||||
for _ in 0..10 {
|
||||
|
@ -87,7 +87,7 @@ impl Device {
|
|||
me.authenticate()?;
|
||||
}
|
||||
|
||||
me.refresh_status()?;
|
||||
me.refresh_status().await?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ impl Device {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn refresh_status(&mut self) -> Result<()> {
|
||||
pub async fn refresh_status(&mut self) -> Result<()> {
|
||||
let mut cmds = vec![self.device_backend.build_query()];
|
||||
|
||||
if self.sub_type == 0 {
|
||||
|
@ -130,7 +130,7 @@ impl Device {
|
|||
let mut buf = [0; 512];
|
||||
let bytes_read = match self.socket.lock().unwrap().read(&mut buf) {
|
||||
Ok(b) => b,
|
||||
Err(_) => break,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if bytes_read == 0 {
|
||||
|
@ -147,11 +147,17 @@ impl Device {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_update<F>(&mut self, f: F)
|
||||
pub async fn set_attribute(&self, attribute: &str, value: AttributeValue) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn register_update<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: Fn(&HashMap<&'static str, AttributeValue>) -> Result<()> + 'static,
|
||||
{
|
||||
self.updates.push(Box::new(f));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn parse_message(&mut self, msg: &[u8]) -> Result<ParseMessage> {
|
||||
|
@ -235,7 +241,7 @@ mod test {
|
|||
use futures::future::try_join;
|
||||
use serial_test::serial;
|
||||
|
||||
use crate::{device::Device, Cloud, Startup};
|
||||
use crate::{device::Device, devices::AttributeValue, Cloud, Startup};
|
||||
|
||||
#[tokio::test]
|
||||
async fn verify_hex() -> Result<()> {
|
||||
|
@ -248,7 +254,7 @@ mod test {
|
|||
let key_hex = b"*[R\x00\xc2\xc0ML\x81\x1d\x05P\xe1\xdc[1CT6\xb9[wM*\x88\xd7\xe4ma\xfd\x96i";
|
||||
|
||||
for device_info in devices {
|
||||
let device = Device::connect(device_info, PY_TOKEN, PY_KEY)?;
|
||||
let device = Device::connect(device_info, PY_TOKEN, PY_KEY).await?;
|
||||
|
||||
assert_eq!(&device.token, token_hex);
|
||||
assert_eq!(&device.key, key_hex);
|
||||
|
@ -265,7 +271,7 @@ mod test {
|
|||
const PY_KEY: &str = "0fc0c56ea8124414a362e6449ee45ba92558a54f159d4937af697e405f2326b9";
|
||||
|
||||
for device_info in devices {
|
||||
Device::connect(device_info, PY_TOKEN, PY_KEY)?;
|
||||
Device::connect(device_info, PY_TOKEN, PY_KEY).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -282,6 +288,7 @@ mod test {
|
|||
let (token, key) = cloud.keys(device_info.id).await?;
|
||||
|
||||
Device::connect(device_info, &token, &key)
|
||||
.await
|
||||
.context(format!("\ntoken: {token}\nkey: {key}"))?;
|
||||
}
|
||||
|
||||
|
@ -303,7 +310,39 @@ mod test {
|
|||
addr: SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(192, 168, 178, 94)), 6444),
|
||||
};
|
||||
|
||||
Device::connect(device_info, token, key)?;
|
||||
Device::connect(device_info, token, key).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn write() -> Result<()> {
|
||||
let token = "dead840607856a4b84c1a9d94ce8f553b50c037b65fa1ca22126de339c367eb765b231b4525d5b8336c48fe5dae38439bbb5e31282ed3790ff98a48049401dca";
|
||||
let key = "50e77947dc63426db3883c7616613410044b02567f5240a5baf789391b2e5a79";
|
||||
|
||||
let device_info = crate::DeviceInfo {
|
||||
id: 152832116426242,
|
||||
model: "760EY015".to_string(),
|
||||
sn: "0000E1541760EY01534091D002581H2R".to_string(),
|
||||
protocol: 3,
|
||||
device_type: 225,
|
||||
addr: SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(192, 168, 178, 94)), 6444),
|
||||
};
|
||||
|
||||
let mut device = Device::connect(device_info, token, key)
|
||||
.await?
|
||||
.register_update(|attributes| {
|
||||
println!("{attributes:?}");
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
device
|
||||
.set_attribute("attribute", AttributeValue::Bool(false))
|
||||
.await?;
|
||||
|
||||
device.refresh_status().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::command::*;
|
|||
use super::{AttributeValue, DeviceBackend};
|
||||
|
||||
pub struct E1 {
|
||||
modes: HashMap<u32, String>,
|
||||
modes: HashMap<u8, String>,
|
||||
attributes: HashMap<&'static str, AttributeValue>,
|
||||
|
||||
status: [&'static str; 5],
|
||||
|
@ -51,15 +51,15 @@ impl E1 {
|
|||
|
||||
let attributes = [
|
||||
("power", AttributeValue::Bool(false)),
|
||||
("status", AttributeValue::Int(0)),
|
||||
("mode", AttributeValue::Int(0)),
|
||||
("status", AttributeValue::String(None)),
|
||||
("mode", AttributeValue::String(None)),
|
||||
("additional", AttributeValue::Int(0)),
|
||||
("uv", AttributeValue::Bool(false)),
|
||||
("dry", AttributeValue::Bool(false)),
|
||||
("dry_status", AttributeValue::Bool(false)),
|
||||
("door", AttributeValue::Bool(false)),
|
||||
("rinse_aid", AttributeValue::Bool(false)),
|
||||
("salt", AttributeValue::Bool(false)),
|
||||
("door_closed", AttributeValue::Bool(false)),
|
||||
("rinse_aid_shortage", AttributeValue::Bool(false)),
|
||||
("salt_shortage", AttributeValue::Bool(false)),
|
||||
("child_lock", AttributeValue::Bool(false)),
|
||||
("storage", AttributeValue::Bool(false)),
|
||||
("storage_progress", AttributeValue::Bool(false)),
|
||||
|
@ -98,7 +98,12 @@ impl DeviceBackend for E1 {
|
|||
}
|
||||
|
||||
fn process_message(&mut self, msg: &[u8]) {
|
||||
CommandE1Response::new(msg).update_attributes(&mut self.attributes);
|
||||
CommandE1Response::new(msg).update_attributes(
|
||||
&mut self.attributes,
|
||||
&self.modes,
|
||||
&self.status,
|
||||
&self.progress,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_attribute(&self, attribute: &str, value: &str) -> () {
|
||||
|
@ -303,7 +308,13 @@ impl CommandE1Response {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_attributes(&mut self, attributes: &mut HashMap<&'static str, AttributeValue>) {
|
||||
pub fn update_attributes(
|
||||
&mut self,
|
||||
attributes: &mut HashMap<&'static str, AttributeValue>,
|
||||
modes: &HashMap<u8, String>,
|
||||
status: &[&str],
|
||||
progress: &[&str],
|
||||
) {
|
||||
let message_type = self.command.header().message_type();
|
||||
let body = self.command.body();
|
||||
let body_type = self.command.body().body_type();
|
||||
|
@ -312,93 +323,155 @@ impl CommandE1Response {
|
|||
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
||||
&& body_type == 0)
|
||||
{
|
||||
attributes.get_mut("power").unwrap().set(body[1] > 0);
|
||||
attributes.get_mut("status").unwrap().set(body[1]);
|
||||
attributes.get_mut("mode").unwrap().set(body[2]);
|
||||
attributes.get_mut("additional").unwrap().set(body[3]);
|
||||
attributes.get_mut("power").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"power",
|
||||
body[1] > 0,
|
||||
);
|
||||
attributes.get_mut("status").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"status",
|
||||
status[body[1] as usize],
|
||||
);
|
||||
attributes.get_mut("mode").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"mode",
|
||||
&modes[&body[2]],
|
||||
);
|
||||
attributes.get_mut("additional").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"additional",
|
||||
body[3],
|
||||
);
|
||||
|
||||
// 0 - open, 1 - close
|
||||
attributes
|
||||
.get_mut("door")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x01) == 0);
|
||||
attributes.get_mut("door_closed").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"door_closed",
|
||||
(body[5] & 0x01) == 0,
|
||||
);
|
||||
|
||||
// 0 - enough, 1 - shortage
|
||||
attributes
|
||||
.get_mut("rinse_aid")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x02) > 0);
|
||||
attributes.get_mut("rinse_aid_shortage").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"rinse_aid_shortage",
|
||||
(body[5] & 0x02) > 0,
|
||||
);
|
||||
|
||||
// e - enough, 1 - shortage
|
||||
attributes
|
||||
.get_mut("salt")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x04) > 0);
|
||||
// 0 - enough, 1 - shortage
|
||||
attributes.get_mut("salt_shortage").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"salt_shortage",
|
||||
(body[5] & 0x04) > 0,
|
||||
);
|
||||
|
||||
let start_pause = (body[5] & 0x08) > 0;
|
||||
|
||||
if start_pause {
|
||||
self.start = true;
|
||||
} else if attributes["status"].byte() == 2 || attributes["status"].byte() == 3 {
|
||||
} else if attributes["status"].str() == Some("Delay")
|
||||
|| attributes["status"].str() == Some("Running")
|
||||
{
|
||||
self.start = false;
|
||||
}
|
||||
|
||||
attributes
|
||||
.get_mut("child_lock")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x10) > 0);
|
||||
attributes.get_mut("uv").unwrap().set((body[4] & 0x02) > 0);
|
||||
attributes.get_mut("dry").unwrap().set((body[4] & 0x10) > 0);
|
||||
attributes
|
||||
.get_mut("dry_status")
|
||||
.unwrap()
|
||||
.set((body[4] & 0x20) > 0);
|
||||
attributes
|
||||
.get_mut("storage")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x20) > 0);
|
||||
attributes
|
||||
.get_mut("storage_progress")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x40) > 0);
|
||||
attributes.get_mut("time_remaining").unwrap().set(body[6]);
|
||||
attributes.get_mut("progress").unwrap().set(body[9]);
|
||||
attributes
|
||||
.get_mut("storage_remaining")
|
||||
.unwrap()
|
||||
.set(if body.len() > 18 {
|
||||
attributes.get_mut("child_lock").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"child_lock",
|
||||
(body[5] & 0x10) > 0,
|
||||
);
|
||||
attributes.get_mut("uv").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"uv",
|
||||
(body[4] & 0x02) > 0,
|
||||
);
|
||||
attributes.get_mut("dry").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"dry",
|
||||
(body[4] & 0x10) > 0,
|
||||
);
|
||||
attributes.get_mut("dry_status").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"dry_status",
|
||||
(body[4] & 0x20) > 0,
|
||||
);
|
||||
attributes.get_mut("storage").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"storage",
|
||||
(body[5] & 0x20) > 0,
|
||||
);
|
||||
attributes.get_mut("storage_progress").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"storage_progress",
|
||||
(body[5] & 0x40) > 0,
|
||||
);
|
||||
attributes.get_mut("time_remaining").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"time_remaining",
|
||||
body[6],
|
||||
);
|
||||
attributes.get_mut("progress").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"progress",
|
||||
progress[body[9] as usize],
|
||||
);
|
||||
attributes.get_mut("storage_remaining").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"storage_remaining",
|
||||
if body.len() > 18 {
|
||||
body[11]
|
||||
} else {
|
||||
false.into()
|
||||
});
|
||||
attributes.get_mut("temperature").unwrap().set(body[11]);
|
||||
attributes
|
||||
.get_mut("humidity")
|
||||
.unwrap()
|
||||
.set(if body.len() > 33 {
|
||||
},
|
||||
);
|
||||
attributes.get_mut("temperature").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"temperature",
|
||||
body[11],
|
||||
);
|
||||
attributes.get_mut("humidity").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"humidity",
|
||||
if body.len() > 33 {
|
||||
body[33].into()
|
||||
} else {
|
||||
AttributeValue::from(None)
|
||||
});
|
||||
attributes
|
||||
.get_mut("waterswitch")
|
||||
.unwrap()
|
||||
.set((body[4] & 0x04) > 0);
|
||||
attributes
|
||||
.get_mut("water_lack")
|
||||
.unwrap()
|
||||
.set((body[5] & 0x80) > 0);
|
||||
attributes.get_mut("error_code").unwrap().set(body[10]);
|
||||
attributes.get_mut("softwater").unwrap().set(body[13]);
|
||||
attributes.get_mut("wrong_operation").unwrap().set(body[16]);
|
||||
attributes
|
||||
.get_mut("bright")
|
||||
.unwrap()
|
||||
.set(if body.len() > 24 {
|
||||
},
|
||||
);
|
||||
attributes.get_mut("waterswitch").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"waterswitch",
|
||||
(body[4] & 0x04) > 0,
|
||||
);
|
||||
attributes.get_mut("water_lack").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"water_lack",
|
||||
(body[5] & 0x80) > 0,
|
||||
);
|
||||
attributes.get_mut("error_code").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"error_code",
|
||||
body[10],
|
||||
);
|
||||
attributes.get_mut("softwater").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"softwater",
|
||||
body[13],
|
||||
);
|
||||
attributes.get_mut("wrong_operation").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"wrong_operation",
|
||||
body[16],
|
||||
);
|
||||
attributes.get_mut("bright").unwrap().set(
|
||||
#[cfg(debug_assertions)]
|
||||
"bright",
|
||||
if body.len() > 24 {
|
||||
body[24].into()
|
||||
} else {
|
||||
AttributeValue::from(None)
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,68 @@ pub enum AttributeValue {
|
|||
}
|
||||
|
||||
impl AttributeValue {
|
||||
pub fn set(&mut self, value: impl Into<Self>) {
|
||||
pub fn set(&mut self, #[cfg(debug_assertions)] attribute_name: &str, value: impl Into<Self>) {
|
||||
match (self, value.into()) {
|
||||
(Self::String(current), Self::String(new)) => *current = new,
|
||||
(Self::String(current), Self::Bool(new)) => *current = Some(new.to_string()),
|
||||
(Self::String(current), Self::Int(new)) => *current = Some(new.to_string()),
|
||||
(Self::Bool(current), Self::Bool(new)) => *current = new,
|
||||
(Self::Int(current), Self::Int(new)) => *current = new,
|
||||
(Self::String(current), Self::String(new)) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if *current != new {
|
||||
println!(
|
||||
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*current = new;
|
||||
}
|
||||
(Self::String(current), Self::Bool(new)) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if *current != Some(new.to_string()) {
|
||||
println!(
|
||||
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*current = Some(new.to_string());
|
||||
}
|
||||
(Self::String(current), Self::Int(new)) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if *current != Some(new.to_string()) {
|
||||
println!(
|
||||
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*current = Some(new.to_string());
|
||||
}
|
||||
(Self::Bool(current), Self::Bool(new)) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if *current != new {
|
||||
println!(
|
||||
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*current = new;
|
||||
}
|
||||
(Self::Int(current), Self::Int(new)) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if *current != new {
|
||||
println!(
|
||||
"Attribute ({attribute_name}) changed from {current:?} to {new:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*current = new;
|
||||
}
|
||||
|
||||
_ => panic!(),
|
||||
}
|
||||
|
@ -46,6 +101,18 @@ impl AttributeValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AttributeValue {
|
||||
fn from(value: &str) -> Self {
|
||||
Self::String(Some(value.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for AttributeValue {
|
||||
fn from(value: &String) -> Self {
|
||||
Self::String(Some(value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AttributeValue {
|
||||
fn from(value: String) -> Self {
|
||||
Self::String(Some(value))
|
||||
|
|
Loading…
Reference in a new issue