rustuya/protocol/
dev22.rs1use crate::crypto::TuyaCipher;
2use crate::error::Result;
3use crate::protocol::{CommandType, TuyaProtocol, Version, create_base_payload};
4use log::trace;
5use serde_json::Value;
6
7pub struct ProtocolDev22 {
8 base: Box<dyn TuyaProtocol>,
9}
10
11impl ProtocolDev22 {
12 #[must_use]
13 pub fn new(base: Box<dyn TuyaProtocol>) -> Self {
14 Self { base }
15 }
16}
17
18impl TuyaProtocol for ProtocolDev22 {
19 fn version(&self) -> Version {
20 self.base.version()
21 }
22
23 fn get_effective_command(&self, command: CommandType) -> u32 {
24 match command {
25 CommandType::DpQuery => CommandType::ControlNew as u32,
26 cmd => cmd as u32,
27 }
28 }
29
30 fn generate_payload(
31 &self,
32 device_id: &str,
33 command: CommandType,
34 data: Option<Value>,
35 cid: Option<&str>,
36 t: u64,
37 ) -> Result<(u32, Value)> {
38 let cmd_to_send = self.get_effective_command(command);
39 let mut payload =
40 create_base_payload(device_id, cid, data.clone(), Some(t.to_string().into()));
41
42 match command {
43 CommandType::UpdateDps => {
44 payload.retain(|k, _| k == "cid");
45 let d = data.unwrap_or_else(|| serde_json::json!([18, 19, 20]));
46 payload.insert("dpId".into(), d);
47 }
48 CommandType::Control | CommandType::ControlNew => {
49 payload.remove("gwId");
50 }
51 CommandType::DpQuery => {
52 payload.remove("gwId");
53 if payload.get("dps").is_none() {
54 payload.insert("dps".into(), serde_json::json!({"1": null}));
55 }
56 }
57 CommandType::DpQueryNew => {
58 payload.remove("gwId");
59 }
60 CommandType::LanExtStream => {
61 payload = data
62 .unwrap_or_else(|| serde_json::json!({}))
63 .as_object()
64 .cloned()
65 .unwrap_or_default();
66 if let Some(c) = cid {
67 payload.insert("cid".into(), c.into());
68 payload.insert("ctype".into(), 0.into());
69 }
70 }
71 CommandType::Status | CommandType::HeartBeat => {
72 payload.remove("uid");
73 payload.remove("t");
74 }
75 _ => {
76 }
78 }
79
80 let payload_obj = Value::Object(payload);
81 trace!("dev22 generated payload (cmd {cmd_to_send}): {payload_obj}");
82
83 Ok((cmd_to_send, payload_obj))
84 }
85
86 fn pack_payload(&self, payload: &[u8], cmd: u32, cipher: &TuyaCipher) -> Result<Vec<u8>> {
87 self.base.pack_payload(payload, cmd, cipher)
88 }
89
90 fn decrypt_payload(&self, payload: Vec<u8>, cipher: &TuyaCipher) -> Result<Vec<u8>> {
91 self.base.decrypt_payload(payload, cipher)
92 }
93
94 fn has_version_header(&self, payload: &[u8]) -> bool {
95 self.base.has_version_header(payload)
96 }
97
98 fn requires_session_key(&self) -> bool {
99 self.base.requires_session_key()
100 }
101
102 fn encrypt_session_key(
103 &self,
104 session_key: &[u8],
105 cipher: &TuyaCipher,
106 nonce: &[u8],
107 ) -> Result<Vec<u8>> {
108 self.base.encrypt_session_key(session_key, cipher, nonce)
109 }
110
111 fn get_prefix(&self) -> u32 {
112 self.base.get_prefix()
113 }
114
115 fn get_hmac_key<'a>(&self, cipher_key: &'a [u8]) -> Option<&'a [u8]> {
116 self.base.get_hmac_key(cipher_key)
117 }
118
119 fn is_empty_payload_allowed(&self, cmd: u32) -> bool {
120 self.base.is_empty_payload_allowed(cmd)
121 }
122
123 fn should_check_dev22_fallback(&self) -> bool {
124 false
125 }
126}