Build E1 commands
This commit is contained in:
parent
631e88aa46
commit
ce87e28c11
7 changed files with 345 additions and 26 deletions
|
@ -1,4 +1,4 @@
|
||||||
use std::ops::Index;
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
use super::header::Header;
|
use super::header::Header;
|
||||||
|
|
||||||
|
@ -21,14 +21,18 @@ impl Body {
|
||||||
|
|
||||||
impl From<&[u8]> for Body {
|
impl From<&[u8]> for Body {
|
||||||
fn from(value: &[u8]) -> Self {
|
fn from(value: &[u8]) -> Self {
|
||||||
Self(
|
let data = value[(Header::HEADER_LENGTH as usize)..value.len() - 1].to_vec();
|
||||||
value[(Header::HEADER_LENGTH as usize)..value.len() - 1]
|
|
||||||
.iter()
|
Self(data)
|
||||||
.cloned()
|
}
|
||||||
.collect::<Vec<u8>>()
|
}
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
impl From<(&[u8], u8)> for Body {
|
||||||
)
|
fn from((data, body_type): (&[u8], u8)) -> Self {
|
||||||
|
let mut v = data.to_vec();
|
||||||
|
v.insert(0, body_type);
|
||||||
|
|
||||||
|
Self(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,3 +43,9 @@ impl Index<usize> for Body {
|
||||||
&self.0[index]
|
&self.0[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> for Body {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,10 +5,8 @@ mod response;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use self::{body::Body, header::Header};
|
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, FromPrimitive)]
|
#[derive(Debug, Clone, Serialize, Deserialize, FromPrimitive, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum MessageType {
|
pub enum MessageType {
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
Set = 0x02,
|
Set = 0x02,
|
||||||
|
@ -33,4 +31,7 @@ pub struct Command {
|
||||||
pub(crate) body: Body,
|
pub(crate) body: Body,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {}
|
pub use body::Body;
|
||||||
|
pub use header::Header;
|
||||||
|
pub use request::*;
|
||||||
|
pub use response::*;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use super::{body::Body, header::Header, Command, MessageType};
|
use super::{body::Body, header::Header, Command, MessageType};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub struct CommandRequest {
|
pub struct CommandRequest {
|
||||||
command: Command,
|
command: Command,
|
||||||
|
@ -28,7 +29,7 @@ impl CommandRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize(&self, body: &[u8]) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
let mut stream = Vec::new();
|
let mut stream = Vec::new();
|
||||||
|
|
||||||
stream.extend_from_slice(self.command.header.raw());
|
stream.extend_from_slice(self.command.header.raw());
|
||||||
|
@ -37,6 +38,18 @@ impl CommandRequest {
|
||||||
|
|
||||||
stream
|
stream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn header(&self) -> &Header {
|
||||||
|
&self.command.header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body(&self) -> &Body {
|
||||||
|
&self.command.body
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body_mut(&mut self) -> &mut Body {
|
||||||
|
&mut self.command.body
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandQuerySubtype {
|
pub struct CommandQuerySubtype {
|
||||||
|
@ -56,6 +69,14 @@ impl CommandQuerySubtype {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandQuerySubtype {
|
||||||
|
type Target = CommandRequest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CommandQueryCustom {
|
pub struct CommandQueryCustom {
|
||||||
command: CommandRequest,
|
command: CommandRequest,
|
||||||
}
|
}
|
||||||
|
@ -67,3 +88,11 @@ impl CommandQueryCustom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandQueryCustom {
|
||||||
|
type Target = CommandRequest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use super::{body::Body, header::Header, Command, MessageType};
|
use super::{body::Body, header::Header, Command, MessageType};
|
||||||
|
|
||||||
pub struct CommandResponse {
|
pub struct CommandResponse {
|
||||||
|
@ -13,6 +15,14 @@ impl CommandResponse {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn header(&self) -> &Header {
|
||||||
|
&self.command.header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body(&self) -> &Body {
|
||||||
|
&self.command.body
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandSubtypeResponse {
|
pub struct CommandSubtypeResponse {
|
||||||
|
@ -44,3 +54,11 @@ impl CommandSubtypeResponse {
|
||||||
Self { command, sub_type }
|
Self { command, sub_type }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandSubtypeResponse {
|
||||||
|
type Target = CommandResponse;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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, MessageType},
|
command::{Command, CommandRequest, MessageType},
|
||||||
devices::{e1::E1, DeviceBackend},
|
devices::{e1::E1, DeviceBackend},
|
||||||
hex,
|
hex,
|
||||||
packet_builder::PacketBuilder,
|
packet_builder::PacketBuilder,
|
||||||
|
@ -112,7 +112,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_status(&mut self) -> Result<()> {
|
pub fn refresh_status(&mut self) -> Result<()> {
|
||||||
let mut cmds = self.device_backend.build_query()?;
|
let mut cmds = vec![self.device_backend.build_query()];
|
||||||
|
|
||||||
if self.sub_type.is_none() {
|
if self.sub_type.is_none() {
|
||||||
cmds.insert(0, Command::sub_type(self.info.device_type));
|
cmds.insert(0, Command::sub_type(self.info.device_type));
|
||||||
|
@ -210,8 +210,9 @@ impl Device {
|
||||||
self.send_message(&msg)
|
self.send_message(&msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_send(&self, cmd: Command) -> Result<()> {
|
fn build_send(&self, cmd: CommandRequest) -> Result<()> {
|
||||||
let data = PacketBuilder::builder(self.info.id, to_string(&cmd)?.as_bytes()).finalize(1);
|
let data = PacketBuilder::builder(self.info.id, to_string(&cmd.serialize())?.as_bytes())
|
||||||
|
.finalize(1);
|
||||||
self.send_message(&data)
|
self.send_message(&data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, ops::Deref};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::command::Command;
|
use crate::command::*;
|
||||||
|
|
||||||
use super::DeviceBackend;
|
use super::DeviceBackend;
|
||||||
|
|
||||||
|
@ -99,7 +99,64 @@ impl DeviceAttributes {
|
||||||
enum AttributeValue {
|
enum AttributeValue {
|
||||||
String(Option<String>),
|
String(Option<String>),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(i32),
|
Int(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttributeValue {
|
||||||
|
pub fn set(&mut self, value: impl Into<Self>) {
|
||||||
|
match (self, value.into()) {
|
||||||
|
(Self::String(current), Self::String(new)) => *current = new,
|
||||||
|
(Self::Bool(current), Self::Bool(new)) => *current = new,
|
||||||
|
(Self::Int(current), Self::Int(new)) => *current = new,
|
||||||
|
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Self::String(opt) => opt.as_ref().map(|s| s.as_str()),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bool(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Bool(bool) => *bool,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn byte(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::Int(b) => *b,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for AttributeValue {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::String(Some(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<String>> for AttributeValue {
|
||||||
|
fn from(value: Option<String>) -> Self {
|
||||||
|
Self::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for AttributeValue {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Self::Bool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for AttributeValue {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
Self::Int(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct E1 {
|
pub struct E1 {
|
||||||
|
@ -191,8 +248,9 @@ impl E1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceBackend for E1 {
|
impl DeviceBackend for E1 {
|
||||||
fn build_query(&self, device_protocol_version: u32) -> Result<Vec<Command>> {
|
fn build_query(&self, device_protocol_version: u32) -> CommandRequest {
|
||||||
Ok(vec![CommandQuery::new(device_protocol_version)])
|
// Ok(vec![CommandQuery::new(device_protocol_version)])
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>> {
|
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
@ -209,3 +267,203 @@ impl DeviceBackend for E1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! concat {
|
||||||
|
($vec1:expr, $($vec2:expr,)+) => {{
|
||||||
|
$(
|
||||||
|
$vec1.extend(&$vec2);
|
||||||
|
)+
|
||||||
|
|
||||||
|
$vec1
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandE1Base {
|
||||||
|
command: CommandRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandE1Base {
|
||||||
|
pub fn new(device_protocol_version: u8, message_type: MessageType, body: Body) -> Self {
|
||||||
|
Self {
|
||||||
|
command: CommandRequest::new(device_protocol_version, 0xE1, message_type, body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandE1Base {
|
||||||
|
type Target = CommandRequest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandPower {
|
||||||
|
command: CommandE1Base,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandPower {
|
||||||
|
pub fn new(device_protocol_version: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
command: CommandE1Base::new(
|
||||||
|
device_protocol_version,
|
||||||
|
MessageType::Set,
|
||||||
|
Body::from(([0x00; 4].as_slice(), 0x08)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power(&self) -> bool {
|
||||||
|
self.body()[1] == 0x01
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_power(&mut self, power: bool) {
|
||||||
|
self.body()[1] = if power { 0x01 } else { 0x00 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandPower {
|
||||||
|
type Target = CommandRequest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandLock {
|
||||||
|
command: CommandE1Base,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandLock {
|
||||||
|
pub fn new(device_protocol_version: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
command: CommandE1Base::new(
|
||||||
|
device_protocol_version,
|
||||||
|
MessageType::Set,
|
||||||
|
Body::from((concat!(vec![0x04], vec![0x00; 36],).as_slice(), 0x08)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self) -> bool {
|
||||||
|
self.body()[1] == 0x03
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_lock(&mut self, lock: bool) {
|
||||||
|
self.body()[1] = if lock { 0x03 } else { 0x04 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandLock {
|
||||||
|
type Target = CommandRequest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandStorage {
|
||||||
|
command: CommandE1Base,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandStorage {
|
||||||
|
pub fn new(device_protocol_version: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
command: CommandE1Base::new(
|
||||||
|
device_protocol_version,
|
||||||
|
MessageType::Set,
|
||||||
|
Body::from((
|
||||||
|
concat!(vec![0x00; 4], vec![0xFF; 6], vec![0x00; 27],).as_slice(),
|
||||||
|
0x81,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn storage(&self) -> bool {
|
||||||
|
self.body()[4] == 0x01
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_storage(&mut self, storage: bool) {
|
||||||
|
self.body()[4] = if storage { 0x01 } else { 0x00 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CommandStorage {
|
||||||
|
type Target = CommandRequest;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.command
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandQuery {
|
||||||
|
command: CommandE1Base,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandQuery {
|
||||||
|
pub fn new(device_protocol_version: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
command: CommandE1Base::new(
|
||||||
|
device_protocol_version,
|
||||||
|
MessageType::Query,
|
||||||
|
Body::from(([].as_slice(), 0x00)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CommandE1Response {
|
||||||
|
command: CommandResponse,
|
||||||
|
start: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandE1Response {
|
||||||
|
pub fn new(msg: &[u8]) -> Self {
|
||||||
|
let command = CommandResponse::new(msg);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
command,
|
||||||
|
start: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_attributes(&self, attributes: &mut HashMap<DeviceAttributes, AttributeValue>) {
|
||||||
|
let message_type = self.command.header().message_type();
|
||||||
|
let body = self.command.body();
|
||||||
|
let body_type = self.command.body().body_type();
|
||||||
|
|
||||||
|
if (message_type == MessageType::Set && 0 <= body_type && body_type <= 7)
|
||||||
|
|| ((message_type == MessageType::Query || message_type == MessageType::Notify1)
|
||||||
|
&& body_type == 0)
|
||||||
|
{
|
||||||
|
attributes[&DeviceAttributes::Power].set(body[1] > 0);
|
||||||
|
attributes[&DeviceAttributes::Status].set(body[1]);
|
||||||
|
attributes[&DeviceAttributes::Mode].set(body[2]);
|
||||||
|
attributes[&DeviceAttributes::Additional].set(body[3]);
|
||||||
|
|
||||||
|
// 0 - open, 1 - close
|
||||||
|
attributes[&DeviceAttributes::Door].set((body[5] & 0x01) == 0);
|
||||||
|
|
||||||
|
// 0 - enough, 1 - shortage
|
||||||
|
attributes[&DeviceAttributes::RinseAid].set((body[5] & 0x02) > 0);
|
||||||
|
|
||||||
|
// e - enough, 1 - shortage
|
||||||
|
attributes[&DeviceAttributes::Salt].set((body[5] & 0x04) > 0);
|
||||||
|
|
||||||
|
let start_pause = (body[5] & 0x08) > 0;
|
||||||
|
|
||||||
|
if start_pause {
|
||||||
|
self.start = true;
|
||||||
|
} else if attributes[&DeviceAttributes::Status].byte() == 2
|
||||||
|
|| attributes[&DeviceAttributes::Status].byte() == 3
|
||||||
|
{
|
||||||
|
self.start = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes[&DeviceAttributes::ChildLock].set((body[5] & 0x10) > 0);
|
||||||
|
attributes[&DeviceAttributes::UV].set((body[4] & 0x2) > 0);
|
||||||
|
attributes[&DeviceAttributes::Dry].set((body[4] & 0x10) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use crate::command::Command;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::command::CommandRequest;
|
||||||
|
|
||||||
pub mod e1;
|
pub mod e1;
|
||||||
|
|
||||||
pub trait DeviceBackend {
|
pub trait DeviceBackend {
|
||||||
fn build_query(&self, device_protocol_version: u32) -> anyhow::Result<Vec<Command>>;
|
fn build_query(&self, device_protocol_version: u32) -> CommandRequest;
|
||||||
fn process_message(&self, msg: &[u8]) -> anyhow::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) -> ();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue