Further device communication work
This commit is contained in:
parent
ce87e28c11
commit
3d37654850
5 changed files with 179 additions and 33 deletions
|
@ -67,6 +67,10 @@ impl CommandQuerySubtype {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request(self) -> CommandRequest {
|
||||||
|
self.command
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for CommandQuerySubtype {
|
impl Deref for CommandQuerySubtype {
|
||||||
|
@ -87,6 +91,10 @@ impl CommandQueryCustom {
|
||||||
command: CommandRequest::new(0, device_type, message_type, body),
|
command: CommandRequest::new(0, device_type, message_type, body),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request(self) -> CommandRequest {
|
||||||
|
self.command
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for CommandQueryCustom {
|
impl Deref for CommandQueryCustom {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use anyhow::{bail, Context, Error, Result};
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
command::{Command, CommandRequest, MessageType},
|
command::{CommandQuerySubtype, CommandRequest, CommandSubtypeResponse, MessageType},
|
||||||
devices::{e1::E1, DeviceBackend},
|
devices::{e1::E1, DeviceBackend},
|
||||||
hex,
|
hex,
|
||||||
packet_builder::PacketBuilder,
|
packet_builder::PacketBuilder,
|
||||||
|
@ -32,7 +32,8 @@ pub struct Device {
|
||||||
device_backend: Box<dyn DeviceBackend>,
|
device_backend: Box<dyn DeviceBackend>,
|
||||||
|
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
sub_type: Option<String>,
|
sub_type: u16,
|
||||||
|
device_protocol_version: u8,
|
||||||
|
|
||||||
updates: Vec<Box<dyn Fn(&[u8]) -> Result<()>>>,
|
updates: Vec<Box<dyn Fn(&[u8]) -> Result<()>>>,
|
||||||
|
|
||||||
|
@ -70,7 +71,8 @@ impl Device {
|
||||||
security: Security::default(),
|
security: Security::default(),
|
||||||
|
|
||||||
buffer: Vec::new(),
|
buffer: Vec::new(),
|
||||||
sub_type: None,
|
sub_type: 0,
|
||||||
|
device_protocol_version: 0,
|
||||||
|
|
||||||
updates: Vec::new(),
|
updates: Vec::new(),
|
||||||
|
|
||||||
|
@ -114,8 +116,8 @@ impl Device {
|
||||||
pub fn refresh_status(&mut self) -> Result<()> {
|
pub fn refresh_status(&mut self) -> Result<()> {
|
||||||
let mut cmds = vec![self.device_backend.build_query()];
|
let mut cmds = vec![self.device_backend.build_query()];
|
||||||
|
|
||||||
if self.sub_type.is_none() {
|
if self.sub_type == 0 {
|
||||||
cmds.insert(0, Command::sub_type(self.info.device_type));
|
cmds.insert(0, CommandQuerySubtype::new(self.info.device_type).request());
|
||||||
}
|
}
|
||||||
|
|
||||||
for cmd in cmds {
|
for cmd in cmds {
|
||||||
|
@ -173,7 +175,7 @@ impl Device {
|
||||||
|
|
||||||
Security::aes_decrypt(crypt);
|
Security::aes_decrypt(crypt);
|
||||||
|
|
||||||
if self.pre_process_message(crypt)? {
|
if self.pre_process_message(crypt) {
|
||||||
let status = self.device_backend.process_message(crypt)?;
|
let status = self.device_backend.process_message(crypt)?;
|
||||||
|
|
||||||
if status.len() > 0 {
|
if status.len() > 0 {
|
||||||
|
@ -188,13 +190,16 @@ impl Device {
|
||||||
Ok(ParseMessage::Success)
|
Ok(ParseMessage::Success)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_process_message(&self, msg: &[u8]) -> Result<bool> {
|
fn pre_process_message(&mut self, msg: &[u8]) -> bool {
|
||||||
if msg[9] == MessageType::QuerySubtype as u8 {
|
if msg[9] == MessageType::QuerySubtype as u8 {
|
||||||
let message = todo!();
|
let message = CommandSubtypeResponse::new(msg);
|
||||||
|
|
||||||
Ok(false)
|
self.sub_type = message.sub_type;
|
||||||
|
self.device_protocol_version = message.header().device_protocol_version();
|
||||||
|
|
||||||
|
false
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::{collections::HashMap, ops::Deref};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
@ -7,7 +10,7 @@ use crate::command::*;
|
||||||
use super::DeviceBackend;
|
use super::DeviceBackend;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
enum DeviceAttributes {
|
pub enum DeviceAttributes {
|
||||||
Power,
|
Power,
|
||||||
Status,
|
Status,
|
||||||
Mode,
|
Mode,
|
||||||
|
@ -96,7 +99,7 @@ impl DeviceAttributes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AttributeValue {
|
pub enum AttributeValue {
|
||||||
String(Option<String>),
|
String(Option<String>),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(u8),
|
Int(u8),
|
||||||
|
@ -106,6 +109,8 @@ impl AttributeValue {
|
||||||
pub fn set(&mut self, value: impl Into<Self>) {
|
pub fn set(&mut self, value: impl Into<Self>) {
|
||||||
match (self, value.into()) {
|
match (self, value.into()) {
|
||||||
(Self::String(current), Self::String(new)) => *current = new,
|
(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::Bool(current), Self::Bool(new)) => *current = new,
|
||||||
(Self::Int(current), Self::Int(new)) => *current = new,
|
(Self::Int(current), Self::Int(new)) => *current = new,
|
||||||
|
|
||||||
|
@ -165,6 +170,8 @@ pub struct E1 {
|
||||||
|
|
||||||
status: [&'static str; 5],
|
status: [&'static str; 5],
|
||||||
progress: [&'static str; 6],
|
progress: [&'static str; 6],
|
||||||
|
|
||||||
|
device_protocol_version: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl E1 {
|
impl E1 {
|
||||||
|
@ -243,17 +250,20 @@ impl E1 {
|
||||||
attributes,
|
attributes,
|
||||||
status,
|
status,
|
||||||
progress,
|
progress,
|
||||||
|
|
||||||
|
device_protocol_version: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceBackend for E1 {
|
impl DeviceBackend for E1 {
|
||||||
fn build_query(&self, device_protocol_version: u32) -> CommandRequest {
|
fn build_query(&self) -> CommandRequest {
|
||||||
// Ok(vec![CommandQuery::new(device_protocol_version)])
|
CommandQuery::new(self.device_protocol_version).request()
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>> {
|
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
let cmd = CommandE1Response::new(msg);
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +308,12 @@ impl Deref for CommandE1Base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DerefMut for CommandE1Base {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CommandPower {
|
pub struct CommandPower {
|
||||||
command: CommandE1Base,
|
command: CommandE1Base,
|
||||||
}
|
}
|
||||||
|
@ -318,7 +334,11 @@ impl CommandPower {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_power(&mut self, power: bool) {
|
pub fn set_power(&mut self, power: bool) {
|
||||||
self.body()[1] = if power { 0x01 } else { 0x00 };
|
self.command.body_mut()[1] = if power { 0x01 } else { 0x00 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(self) -> CommandRequest {
|
||||||
|
self.command.command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +370,11 @@ impl CommandLock {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_lock(&mut self, lock: bool) {
|
pub fn set_lock(&mut self, lock: bool) {
|
||||||
self.body()[1] = if lock { 0x03 } else { 0x04 }
|
self.command.body_mut()[1] = if lock { 0x03 } else { 0x04 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(self) -> CommandRequest {
|
||||||
|
self.command.command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +409,11 @@ impl CommandStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_storage(&mut self, storage: bool) {
|
pub fn set_storage(&mut self, storage: bool) {
|
||||||
self.body()[4] = if storage { 0x01 } else { 0x00 }
|
self.command.body_mut()[4] = if storage { 0x01 } else { 0x00 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(self) -> CommandRequest {
|
||||||
|
self.command.command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,6 +439,10 @@ impl CommandQuery {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn request(self) -> CommandRequest {
|
||||||
|
self.command.command
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandE1Response {
|
pub struct CommandE1Response {
|
||||||
|
@ -428,7 +460,10 @@ impl CommandE1Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_attributes(&self, attributes: &mut HashMap<DeviceAttributes, AttributeValue>) {
|
pub fn update_attributes(
|
||||||
|
&mut self,
|
||||||
|
attributes: &mut HashMap<DeviceAttributes, AttributeValue>,
|
||||||
|
) {
|
||||||
let message_type = self.command.header().message_type();
|
let message_type = self.command.header().message_type();
|
||||||
let body = self.command.body();
|
let body = self.command.body();
|
||||||
let body_type = self.command.body().body_type();
|
let body_type = self.command.body().body_type();
|
||||||
|
@ -437,19 +472,40 @@ impl CommandE1Response {
|
||||||
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
||||||
&& body_type == 0)
|
&& body_type == 0)
|
||||||
{
|
{
|
||||||
attributes[&DeviceAttributes::Power].set(body[1] > 0);
|
attributes
|
||||||
attributes[&DeviceAttributes::Status].set(body[1]);
|
.get_mut(&DeviceAttributes::Power)
|
||||||
attributes[&DeviceAttributes::Mode].set(body[2]);
|
.unwrap()
|
||||||
attributes[&DeviceAttributes::Additional].set(body[3]);
|
.set(body[1] > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Status)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[1]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Mode)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[2]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Additional)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[3]);
|
||||||
|
|
||||||
// 0 - open, 1 - close
|
// 0 - open, 1 - close
|
||||||
attributes[&DeviceAttributes::Door].set((body[5] & 0x01) == 0);
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Door)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[5] & 0x01) == 0);
|
||||||
|
|
||||||
// 0 - enough, 1 - shortage
|
// 0 - enough, 1 - shortage
|
||||||
attributes[&DeviceAttributes::RinseAid].set((body[5] & 0x02) > 0);
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::RinseAid)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[5] & 0x02) > 0);
|
||||||
|
|
||||||
// e - enough, 1 - shortage
|
// e - enough, 1 - shortage
|
||||||
attributes[&DeviceAttributes::Salt].set((body[5] & 0x04) > 0);
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Salt)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[5] & 0x04) > 0);
|
||||||
|
|
||||||
let start_pause = (body[5] & 0x08) > 0;
|
let start_pause = (body[5] & 0x08) > 0;
|
||||||
|
|
||||||
|
@ -461,9 +517,86 @@ impl CommandE1Response {
|
||||||
self.start = false;
|
self.start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes[&DeviceAttributes::ChildLock].set((body[5] & 0x10) > 0);
|
attributes
|
||||||
attributes[&DeviceAttributes::UV].set((body[4] & 0x2) > 0);
|
.get_mut(&DeviceAttributes::ChildLock)
|
||||||
attributes[&DeviceAttributes::Dry].set((body[4] & 0x10) > 0);
|
.unwrap()
|
||||||
|
.set((body[5] & 0x10) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::UV)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[4] & 0x02) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Dry)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[4] & 0x10) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::DryStatus)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[4] & 0x20) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Status)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[5] & 0x20) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::StorageStatus)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[5] & 0x40) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::TimeRemaining)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[6]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Progress)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[9]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::StorageRemaining)
|
||||||
|
.unwrap()
|
||||||
|
.set(if body.len() > 18 {
|
||||||
|
body[11]
|
||||||
|
} else {
|
||||||
|
false.into()
|
||||||
|
});
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Temperature)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[11]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Humidity)
|
||||||
|
.unwrap()
|
||||||
|
.set(if body.len() > 33 {
|
||||||
|
body[33].into()
|
||||||
|
} else {
|
||||||
|
AttributeValue::from(None)
|
||||||
|
});
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Waterswitch)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[4] & 0x04) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::WaterLack)
|
||||||
|
.unwrap()
|
||||||
|
.set((body[5] & 0x80) > 0);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::ErrorCode)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[10]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Softwater)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[13]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::WrongOperation)
|
||||||
|
.unwrap()
|
||||||
|
.set(body[16]);
|
||||||
|
attributes
|
||||||
|
.get_mut(&DeviceAttributes::Bright)
|
||||||
|
.unwrap()
|
||||||
|
.set(if body.len() > 24 {
|
||||||
|
body[24].into()
|
||||||
|
} else {
|
||||||
|
AttributeValue::from(None)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::command::CommandRequest;
|
||||||
pub mod e1;
|
pub mod e1;
|
||||||
|
|
||||||
pub trait DeviceBackend {
|
pub trait DeviceBackend {
|
||||||
fn build_query(&self, device_protocol_version: u32) -> CommandRequest;
|
fn build_query(&self) -> CommandRequest;
|
||||||
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>>;
|
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>>;
|
||||||
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub struct DeviceInfo {
|
||||||
pub model: String,
|
pub model: String,
|
||||||
pub sn: String,
|
pub sn: String,
|
||||||
pub protocol: u8,
|
pub protocol: u8,
|
||||||
pub device_type: u32,
|
pub device_type: u8,
|
||||||
|
|
||||||
pub addr: SocketAddr,
|
pub addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ impl Startup {
|
||||||
let upper = start + encrypt_data[40] as usize;
|
let upper = start + encrypt_data[40] as usize;
|
||||||
let ssid = from_utf8(&encrypt_data[start..upper])?;
|
let ssid = from_utf8(&encrypt_data[start..upper])?;
|
||||||
|
|
||||||
let device_type = u32::from_str_radix(ssid.split('_').nth(1).unwrap(), 16)?;
|
let device_type = u8::from_str_radix(ssid.split('_').nth(1).unwrap(), 16)?;
|
||||||
let model = from_utf8(&encrypt_data[17..25])?;
|
let model = from_utf8(&encrypt_data[17..25])?;
|
||||||
let sn = from_utf8(&encrypt_data[8..40])?;
|
let sn = from_utf8(&encrypt_data[8..40])?;
|
||||||
let port = Self::bytes_to_port(&encrypt_data[4..8]);
|
let port = Self::bytes_to_port(&encrypt_data[4..8]);
|
||||||
|
|
Loading…
Reference in a new issue