1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
mod cipher;
mod crc;
pub mod error;
pub mod mesparse;
pub mod tuyadevice;

extern crate num;
extern crate num_derive;
#[macro_use]
extern crate lazy_static;

use serde::{Deserialize, Serialize};
use std::time::SystemTime;

use crate::mesparse::Result;
use std::collections::HashMap;

pub enum TuyaType {
    Socket,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct Payload {
    devId: String,
    gwId: String,
    uid: String,
    t: u32,
    dps: HashMap<String, serde_json::Value>,
}

#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct GetPayload {
    devId: String,
    gwId: String,
}

// Convenience method to create a valid Tuya style payload from a device ID and a state received
// from mqtt.
// Calling:
//
// payload("abcde", TuyaType::Socket, "on");
//
// will render:
//
// {
//   devId: abcde,
//   gwId: abcde,
//   uid: "",
//   t: 132478194, <-- current time
//   dps: {
//     1: true
//   }
// }
//
pub fn payload(device_id: &str, tt: TuyaType, state: &str) -> Result<String> {
    serde_json::to_string(&Payload {
        devId: device_id.to_string(),
        gwId: device_id.to_string(),
        uid: "".to_string(),
        t: SystemTime::now()
            .duration_since(SystemTime::UNIX_EPOCH)
            .map_err(error::ErrorKind::SystemTimeError)?
            .as_secs() as u32,
        dps: dps(tt, state),
    })
    .map_err(error::ErrorKind::JsonError)
}

pub fn get_payload(device_id: &str) -> Result<String> {
    serde_json::to_string(&GetPayload {
        devId: device_id.to_string(),
        gwId: device_id.to_string(),
    })
    .map_err(error::ErrorKind::JsonError)
}

fn dps(tt: TuyaType, state: &str) -> HashMap<String, serde_json::Value> {
    match tt {
        TuyaType::Socket => socket_dps(state),
    }
}

fn socket_dps(state: &str) -> HashMap<String, serde_json::Value> {
    let mut map = HashMap::new();
    if state.eq_ignore_ascii_case("on") || state.eq_ignore_ascii_case("1") {
        map.insert("1".to_string(), serde_json::to_value(true).unwrap());
    } else {
        map.insert("1".to_string(), serde_json::to_value(false).unwrap());
    }
    map
}