use std::fmt::Debug;
use std::net::IpAddr;
use serde::de;
use serde_derive::{Serialize, Deserialize};
use base64::{Engine as _, engine::general_purpose};
use aes::Aes128;
use aes::cipher::{BlockEncrypt, BlockDecrypt, KeyInit, generic_array::GenericArray };
use serde_json::Value;
use crate::*;
type Int = i32;
pub mod vars {
pub type VarName = &'static str;
#[repr(i32)]
pub enum OnOff {
Off = 0,
On = 1
}
pub const POW: VarName = "Pow";
pub type Pow = OnOff;
pub const MOD: VarName = "Mod";
#[repr(i32)]
pub enum Mod {
Auto = 0,
Cool = 1,
Dry = 2,
Fan = 3,
Heat = 4,
}
pub const SET_TEM: VarName = "SetTem";
pub const TEM_UN: VarName = "TemUn";
#[repr(i32)]
pub enum TemUn {
Celsius = 0,
Fahrenheit = 1,
}
pub const WD_SPD: VarName = "WdSpd";
#[repr(i32)]
pub enum WdSpd {
Auto = 0,
Low = 1,
MediumLow = 2,
Medium = 3,
MediumHigh = 4,
High = 5,
}
pub const AIR: VarName = "Air";
pub type Air = OnOff;
pub const BLO: &str = "Blo";
pub type Blo = OnOff;
pub const HEALTH: VarName = "Health";
pub type Health = OnOff;
pub const SWH_SLP: VarName = "SwhSlp";
pub type SwhSlp = OnOff;
pub const LIG: VarName = "Lig";
pub type Lig = OnOff;
pub const SWING_LF_RIG: VarName = "SwingLfRig";
#[repr(i32)]
pub enum SwingLfRig {
Default = 0,
Full = 1,
Pos0 = 2,
Pos1 = 3,
Pos2 = 4,
Pos3 = 5,
Pos4 = 6
}
pub const SW_UP_DN: VarName = "SwUpDn";
#[repr(i32)]
pub enum SwUpDn {
Default = 0,
Full = 1,
Fixed1 = 2,
Fixed2 = 3,
Fixed3 = 4,
Fixed4 = 5,
Fixed5 = 6,
Swing5 = 7,
Swing4 = 8,
Swing3 = 9,
Swing2 = 10,
Swing1 = 11
}
pub const QUIET: VarName = "Quiet";
pub type Quiet = OnOff;
pub const TUR: VarName = "Tur";
pub type Tur = OnOff;
pub const ST_HT: VarName = "StHt";
pub const HEAT_COOL_TYPE: VarName = "HeatCoolType";
pub const TEM_REC: VarName = "TemRec";
pub const SV_ST: VarName = "SvSt";
pub type SvSt = OnOff;
pub const TEM_SEN: VarName = "TemSen";
pub const TIME: VarName = "time";
pub const ALL: [VarName; 20] = [
POW,
MOD,
SET_TEM,
TEM_UN,
WD_SPD,
AIR,
BLO,
HEALTH,
SWH_SLP,
LIG,
SWING_LF_RIG,
SW_UP_DN,
QUIET,
TUR,
ST_HT,
HEAT_COOL_TYPE,
TEM_REC,
SV_ST,
TEM_SEN,
TIME,
];
pub fn name_of(n: &str) -> Option<VarName> {
match n {
POW => Some(POW),
MOD => Some(MOD),
SET_TEM => Some(SET_TEM),
TEM_UN => Some(TEM_UN),
WD_SPD => Some(WD_SPD),
AIR => Some(AIR),
BLO => Some(BLO),
HEALTH => Some(HEALTH),
SWH_SLP => Some(SWH_SLP),
LIG => Some(LIG),
SWING_LF_RIG => Some(SWING_LF_RIG),
SW_UP_DN => Some(SW_UP_DN),
QUIET => Some(QUIET),
TUR => Some(TUR),
ST_HT => Some(ST_HT),
HEAT_COOL_TYPE => Some(HEAT_COOL_TYPE),
TEM_REC => Some(TEM_REC),
SV_ST => Some(SV_ST),
TEM_SEN => Some(TEM_SEN),
TIME => Some(TIME),
_ => None,
}
}
use crate::{Result, Value, Error};
pub fn parse_value(name: VarName, value: impl AsRef<str>) -> Result<Value> {
Ok(match name {
TIME => {
Value::String(value.as_ref().to_owned())
}
POW | TEM_UN | AIR | BLO | HEALTH | SWH_SLP | LIG | QUIET | TUR | SV_ST | ST_HT => {
let w: u8 = value.as_ref().parse()?;
if w > 1 { return Err(Error::invalid_value(name, value.as_ref())) }
Value::Number(w.into())
}
MOD | SET_TEM | TEM_REC | WD_SPD | SWING_LF_RIG | SW_UP_DN => {
let w: u8 = value.as_ref().parse()?;
Value::Number(w.into())
}
_ => {
value.as_ref().into()
}
})
}
}
pub const SCAN_MESSAGE: &[u8] = br#"{
"t": "scan"
}"#;
#[derive(Deserialize, Debug)]
pub struct GenericMessage {
#[serde(default)]
pub cid: String,
#[serde(default)]
pub i: Int,
#[serde(default)]
pub pack: String,
#[serde(default)]
pub t: String,
#[serde(default)]
pub tcid: String,
#[serde(default)]
pub uid: Int,
}
#[derive(Serialize)]
pub struct GenericOutMessage<'t> {
pub cid: &'t str,
pub i: Int,
pub pack: String,
pub t: &'t str,
pub tcid: &'t str,
pub uid: Int,
}
#[derive(Deserialize, Debug)]
pub struct ScanResponsePack {
#[serde(default)]
pub t: String,
#[serde(default)]
pub cid: String,
#[serde(default)]
pub bc: String,
#[serde(default)]
pub brand: String,
#[serde(default)]
pub catalog:String,
#[serde(default)]
pub mac: String,
#[serde(default)]
pub mid: String,
#[serde(default)]
pub model: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub lock: i32,
#[serde(default)]
pub series: String,
#[serde(default)]
pub vender: String,
#[serde(default)]
pub ver: String
}
pub fn scan_request() -> &'static [u8] { SCAN_MESSAGE }
#[derive(Serialize)]
pub struct BindRequestPack<'t> {
mac: &'t str,
t: &'t str,
uid: Int,
}
#[derive(Debug, Deserialize)]
pub struct BindResponsePack {
pub t: String,
pub mac: String,
pub key: String,
pub r: Int
}
pub fn bind_request<'t>(mac: &'t str, key: &str) -> Result<GenericOutMessage<'t>> {
let pack = serde_json::to_vec(&BindRequestPack {
mac,
t: "bind",
uid: 0
})?;
let pack = encode_request(pack, key.as_bytes());
Ok(GenericOutMessage {
cid: "app",
i: 1,
pack,
t: "pack",
tcid: mac,
uid: 0
})
}
#[derive(Serialize)]
pub struct StatusRequestPack<'t> {
cols: &'t[&'t str],
mac: &'t str,
t: &'t str,
}
#[derive(Debug, Deserialize)]
pub struct StatusResponsePack {
pub t: String,
pub mac: String,
pub r: Int,
pub cols: Vec<String>,
pub dat: Vec<Value>,
}
pub fn status_request<'t>(mac: &'t str, key: &str, variables: &[&str]) -> Result<GenericOutMessage<'t>> {
let pack = serde_json::to_vec(&StatusRequestPack {
cols: variables,
mac,
t: "status",
})?;
let pack = encode_request(pack, key.as_bytes());
Ok(GenericOutMessage {
cid: "app",
i: 0,
pack,
t: "pack",
tcid: mac,
uid: 0
})
}
#[derive(Serialize)]
pub struct CommandPack<'t> {
opt: &'t[&'t str],
p: &'t[Value],
t: &'t str,
}
#[derive(Debug, Deserialize)]
pub struct CommandResponsePack {
pub t: String,
pub mac: String,
pub r: Int,
pub opt: Vec<String>,
pub p: Vec<Value>,
#[serde(default)]
pub val: Vec<Value>,
}
pub fn setvar_request<'t>(mac: &'t str, key: &str, names: &[&str], values: &[Value]) -> Result<GenericOutMessage<'t>> {
let pack = serde_json::to_vec(&CommandPack {
opt: names,
p: values,
t: "cmd",
})?;
let pack = encode_request(pack, key.as_bytes());
Ok(GenericOutMessage {
cid: "app",
i: 0,
pack,
t: "pack",
tcid: mac,
uid: 0
})
}
pub fn handle_response<T: de::DeserializeOwned + Debug>(addr: IpAddr, pack:&str, key: &str) -> Result<T> {
let pack = decode_response(pack, key)?;
trace!("[{}] pack raw: {}", addr, pack);
let pack: T = serde_json::from_str(&pack)?;
debug!("[{}] pack: {:?}", addr, pack);
Ok(pack)
}
fn pkcs7_unpad(payload: &mut Vec<u8>) {
if let Some(b) = payload.last() {
for _ in 0..*b {
payload.pop();
}
}
}
fn pkcs7_pad(payload: &mut Vec<u8>, blocksize: u8) {
let pad_len = blocksize - ((payload.len() % (blocksize as usize)) as u8);
for _ in 0..pad_len {
payload.push(pad_len);
}
}
pub fn decode_response(pack: &str, key: &str) -> Result<String> {
let key = GenericArray::clone_from_slice(key.as_bytes());
let cipher = Aes128::new(&key);
let blocksize = 16;
let mut payload = general_purpose::STANDARD.decode(pack)?;
for pos in (0..payload.len()).step_by(blocksize) {
let slice = &mut payload[pos..pos+blocksize];
let mut block = GenericArray::clone_from_slice(slice);
cipher.decrypt_block(&mut block);
slice.copy_from_slice(block.as_slice())
}
pkcs7_unpad(&mut payload);
Ok(String::from_utf8_lossy(&payload).to_string())
}
pub fn encode_request(mut payload: Vec<u8>, key: &[u8]) -> String {
let key = GenericArray::clone_from_slice(key);
let cipher = Aes128::new(&key);
let blocksize = 16;
pkcs7_pad(&mut payload, blocksize as u8);
for pos in (0..payload.len()).step_by(blocksize) {
let slice = &mut payload[pos..pos+blocksize];
let mut block = GenericArray::clone_from_slice(slice);
cipher.encrypt_block(&mut block);
slice.copy_from_slice(block.as_slice())
}
general_purpose::STANDARD.encode(payload)
}