Compare commits
22 commits
2150e2b2b3
...
ff1888538d
Author | SHA1 | Date | |
---|---|---|---|
ff1888538d | |||
c5eb1b5eff | |||
405b750482 | |||
e9df623c3b | |||
6dab56ba02 | |||
9a2df58f5c | |||
33ea4ab4b6 | |||
88d511daa3 | |||
14a6cd23c6 | |||
f7709686e1 | |||
b824f76995 | |||
3db9029d00 | |||
3f595f5c3b | |||
8ea4fc1aef | |||
f8fa859c29 | |||
56fe4beb2a | |||
2cf725b5aa | |||
e614c2a753 | |||
502790823e | |||
613dc3c530 | |||
07a23ab18d | |||
9660de01aa |
4 changed files with 102 additions and 36 deletions
18
Cargo.toml
18
Cargo.toml
|
@ -7,22 +7,22 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||
if-addrs = "0.10.2"
|
||||
if-addrs = "0.11.0"
|
||||
rand = "0.8.5"
|
||||
chrono = "0.4.31"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.107"
|
||||
reqwest = "0.11.20"
|
||||
tokio = { version = "1.32.0", features=["macros", "rt-multi-thread"] }
|
||||
futures = "0.3.28"
|
||||
serde_json = "1.0.108"
|
||||
reqwest = "0.11.22"
|
||||
tokio = { version = "1.34.0", features=["macros", "rt-multi-thread"] }
|
||||
futures = "0.3.29"
|
||||
serial_test = "2.0.0"
|
||||
num-traits = "0.2.0"
|
||||
num-derive = "0.4.0"
|
||||
num-traits = "0.2.17"
|
||||
num-derive = "0.4.1"
|
||||
|
||||
#crypto
|
||||
cbc = { version = "0.1.2", features = ["alloc"] }
|
||||
md5 = "0.7.0"
|
||||
base64 = "0.21.4"
|
||||
base64 = "0.21.5"
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.7"
|
||||
sha2 = "0.10.8"
|
||||
aes = "0.8.3"
|
||||
|
|
|
@ -2,6 +2,9 @@ use super::{body::Body, header::Header, Command, MessageType};
|
|||
use std::ops::Deref;
|
||||
|
||||
pub trait RequestSerializer {
|
||||
fn message_type(&self) -> MessageType;
|
||||
fn body_type(&self) -> u8;
|
||||
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
|
@ -36,6 +39,7 @@ impl CommandRequest {
|
|||
body,
|
||||
},
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +69,14 @@ impl RequestSerializer for CommandRequest {
|
|||
|
||||
stream
|
||||
}
|
||||
|
||||
fn message_type(&self) -> MessageType {
|
||||
self.header().message_type()
|
||||
}
|
||||
|
||||
fn body_type(&self) -> u8 {
|
||||
self.body().body_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandQuerySubtype {
|
||||
|
@ -102,6 +114,14 @@ impl RequestSerializer for CommandQuerySubtype {
|
|||
fn serialize(&self) -> Vec<u8> {
|
||||
self.command.serialize()
|
||||
}
|
||||
|
||||
fn message_type(&self) -> MessageType {
|
||||
self.header().message_type()
|
||||
}
|
||||
|
||||
fn body_type(&self) -> u8 {
|
||||
self.body().body_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandQueryCustom {
|
||||
|
@ -140,6 +160,14 @@ impl RequestSerializer for CommandQueryCustom {
|
|||
fn serialize(&self) -> Vec<u8> {
|
||||
self.command.serialize()
|
||||
}
|
||||
|
||||
fn message_type(&self) -> MessageType {
|
||||
self.header().message_type()
|
||||
}
|
||||
|
||||
fn body_type(&self) -> u8 {
|
||||
self.body().body_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandHeartbeat;
|
||||
|
@ -148,4 +176,12 @@ impl RequestSerializer for CommandHeartbeat {
|
|||
fn serialize(&self) -> Vec<u8> {
|
||||
vec![0x00]
|
||||
}
|
||||
|
||||
fn message_type(&self) -> MessageType {
|
||||
MessageType::None
|
||||
}
|
||||
|
||||
fn body_type(&self) -> u8 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,24 +124,22 @@ impl Device {
|
|||
}
|
||||
|
||||
for cmd in cmds {
|
||||
'sending: loop {
|
||||
self.build_send(cmd.clone())?;
|
||||
self.build_send(cmd.clone())?;
|
||||
|
||||
loop {
|
||||
let mut buf = [0; 512];
|
||||
let bytes_read = match self.socket.lock().unwrap().read(&mut buf) {
|
||||
Ok(b) => b,
|
||||
Err(_) => continue,
|
||||
};
|
||||
loop {
|
||||
let mut buf = [0; 512];
|
||||
let bytes_read = match self.socket.lock().unwrap().read(&mut buf) {
|
||||
Ok(b) => b,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
if bytes_read == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
match self.parse_message(&buf[..bytes_read])? {
|
||||
ParseMessage::Success => break 'sending,
|
||||
ParseMessage::Padding => continue,
|
||||
}
|
||||
match self.parse_message(&buf[..bytes_read])? {
|
||||
ParseMessage::Success => break,
|
||||
ParseMessage::Padding => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +224,11 @@ impl Device {
|
|||
fn send_message(&self, msg: &[u8]) -> Result<()> {
|
||||
let data = self.security.encode_8370(msg, MsgType::ENCRYPTED_REQUEST)?;
|
||||
|
||||
self.socket.lock().unwrap().write(&data)?;
|
||||
self.socket
|
||||
.lock()
|
||||
.unwrap()
|
||||
.write(&data)
|
||||
.context(format!("{msg:?}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -237,15 +239,26 @@ impl Device {
|
|||
}
|
||||
|
||||
fn build_send(&self, cmd: impl RequestSerializer) -> Result<()> {
|
||||
let message_type = cmd.message_type();
|
||||
let body_type = cmd.body_type();
|
||||
|
||||
let data = PacketBuilder::builder(self.info.id, cmd).finalize(1);
|
||||
|
||||
self.send_message(&data)
|
||||
Ok(self.send_message(&data).context(format!(
|
||||
"message_type: {message_type:?}, body_type: {body_type}"
|
||||
))?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::{
|
||||
net::{Ipv4Addr, SocketAddr},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use futures::future::try_join;
|
||||
|
@ -342,19 +355,29 @@ mod test {
|
|||
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:?}");
|
||||
let power: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
|
||||
|
||||
Ok(())
|
||||
let device = Device::connect(device_info, token, key)
|
||||
.await?
|
||||
.register_update({
|
||||
let power = power.clone();
|
||||
|
||||
move |attributes| {
|
||||
println!("{attributes:?}");
|
||||
|
||||
power.store(attributes["power"].bool(), Ordering::SeqCst);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
device
|
||||
.set_attribute("attribute", AttributeValue::Bool(false))
|
||||
.set_attribute("power", AttributeValue::Bool(true))
|
||||
.await?;
|
||||
|
||||
device.refresh_status().await?;
|
||||
device
|
||||
.set_attribute("power", AttributeValue::Bool(false))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ impl DeviceBackend for E1 {
|
|||
cmd.request()
|
||||
}
|
||||
|
||||
_ => todo!(),
|
||||
_ => todo!("attribute: {attribute}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,14 @@ impl CommandE1Base {
|
|||
#[cfg(debug_assertions)] name: &'static str,
|
||||
) -> Self {
|
||||
Self {
|
||||
command: CommandRequest::new(device_protocol_version, 0xE1, message_type, body, name),
|
||||
command: CommandRequest::new(
|
||||
device_protocol_version,
|
||||
0xE1,
|
||||
message_type,
|
||||
body,
|
||||
#[cfg(debug_assertions)]
|
||||
name,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue