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;
|
||||
|
||||
|
@ -21,14 +21,18 @@ impl Body {
|
|||
|
||||
impl From<&[u8]> for Body {
|
||||
fn from(value: &[u8]) -> Self {
|
||||
Self(
|
||||
value[(Header::HEADER_LENGTH as usize)..value.len() - 1]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
let data = value[(Header::HEADER_LENGTH as usize)..value.len() - 1].to_vec();
|
||||
|
||||
Self(data)
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
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 self::{body::Body, header::Header};
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromPrimitive)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, FromPrimitive, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum MessageType {
|
||||
None = 0x00,
|
||||
Set = 0x02,
|
||||
|
@ -33,4 +31,7 @@ pub struct Command {
|
|||
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 std::ops::Deref;
|
||||
|
||||
pub struct CommandRequest {
|
||||
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();
|
||||
|
||||
stream.extend_from_slice(self.command.header.raw());
|
||||
|
@ -37,6 +38,18 @@ impl CommandRequest {
|
|||
|
||||
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 {
|
||||
|
@ -56,6 +69,14 @@ impl CommandQuerySubtype {
|
|||
}
|
||||
}
|
||||
|
||||
impl Deref for CommandQuerySubtype {
|
||||
type Target = CommandRequest;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.command
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandQueryCustom {
|
||||
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};
|
||||
|
||||
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 {
|
||||
|
@ -44,3 +54,11 @@ impl CommandSubtypeResponse {
|
|||
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 crate::{
|
||||
command::{Command, MessageType},
|
||||
command::{Command, CommandRequest, MessageType},
|
||||
devices::{e1::E1, DeviceBackend},
|
||||
hex,
|
||||
packet_builder::PacketBuilder,
|
||||
|
@ -112,7 +112,7 @@ impl Device {
|
|||
}
|
||||
|
||||
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() {
|
||||
cmds.insert(0, Command::sub_type(self.info.device_type));
|
||||
|
@ -210,8 +210,9 @@ impl Device {
|
|||
self.send_message(&msg)
|
||||
}
|
||||
|
||||
fn build_send(&self, cmd: Command) -> Result<()> {
|
||||
let data = PacketBuilder::builder(self.info.id, to_string(&cmd)?.as_bytes()).finalize(1);
|
||||
fn build_send(&self, cmd: CommandRequest) -> Result<()> {
|
||||
let data = PacketBuilder::builder(self.info.id, to_string(&cmd.serialize())?.as_bytes())
|
||||
.finalize(1);
|
||||
self.send_message(&data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, ops::Deref};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::command::Command;
|
||||
use crate::command::*;
|
||||
|
||||
use super::DeviceBackend;
|
||||
|
||||
|
@ -99,7 +99,64 @@ impl DeviceAttributes {
|
|||
enum AttributeValue {
|
||||
String(Option<String>),
|
||||
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 {
|
||||
|
@ -191,8 +248,9 @@ impl E1 {
|
|||
}
|
||||
|
||||
impl DeviceBackend for E1 {
|
||||
fn build_query(&self, device_protocol_version: u32) -> Result<Vec<Command>> {
|
||||
Ok(vec![CommandQuery::new(device_protocol_version)])
|
||||
fn build_query(&self, device_protocol_version: u32) -> CommandRequest {
|
||||
// Ok(vec![CommandQuery::new(device_protocol_version)])
|
||||
todo!()
|
||||
}
|
||||
|
||||
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 trait DeviceBackend {
|
||||
fn build_query(&self, device_protocol_version: u32) -> anyhow::Result<Vec<Command>>;
|
||||
fn process_message(&self, msg: &[u8]) -> anyhow::Result<Vec<u8>>;
|
||||
fn build_query(&self, device_protocol_version: u32) -> CommandRequest;
|
||||
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>>;
|
||||
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue