rust_tuyapi/
lib.rs
1mod cipher;
43mod crc;
44pub mod error;
45pub mod mesparse;
46pub mod transports;
47pub mod tuyadevice;
48
49extern crate num;
50extern crate num_derive;
51#[macro_use]
52extern crate lazy_static;
53
54use serde::{Deserialize, Serialize};
55
56use std::collections::HashMap;
57use std::convert::TryFrom;
58use std::fmt::Display;
59
60use crate::error::ErrorKind;
61use std::convert::TryInto;
62
63pub use transports::Transport;
64pub use tuyadevice::TuyaDevice;
65
66pub type Result<T> = std::result::Result<T, ErrorKind>;
67#[derive(Debug, Clone, PartialEq)]
70pub enum Payload {
71 Struct(PayloadStruct),
72 String(String),
73}
74
75#[derive(Debug, Clone, PartialEq)]
76pub enum DpId {
77 Lower,
78 Higher,
79}
80
81impl DpId {
82 fn get_ids(self) -> Vec<u8> {
83 match self {
84 DpId::Lower => vec![4, 5, 6],
85 DpId::Higher => vec![18, 19, 20],
86 }
87 }
88}
89
90impl Payload {
91 pub fn new(
92 dev_id: String,
93 gw_id: Option<String>,
94 uid: Option<String>,
95 t: Option<u32>,
96 dp_id: Option<DpId>,
97 dps: Option<HashMap<String, serde_json::Value>>,
98 ) -> Payload {
99 Payload::Struct(PayloadStruct {
100 dev_id,
101 gw_id,
102 uid,
103 t,
104 dp_id: dp_id.map(DpId::get_ids),
105 dps,
106 })
107 }
108}
109
110impl Display for Payload {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 Payload::Struct(s) => write!(f, "{}", s),
114 Payload::String(s) => write!(f, "{}", s),
115 }
116 }
117}
118
119#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
122pub struct PayloadStruct {
123 #[serde(rename = "devId")]
124 pub dev_id: String,
125 #[serde(rename = "gwId", skip_serializing_if = "Option::is_none")]
126 pub gw_id: Option<String>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub uid: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub t: Option<u32>,
131 #[serde(rename = "dpId", skip_serializing_if = "Option::is_none")]
132 pub dp_id: Option<Vec<u8>>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub dps: Option<HashMap<String, serde_json::Value>>,
135}
136
137pub trait Truncate {
139 fn truncate(&self) -> Self;
140
141 fn truncate_str(text: &str) -> &str {
143 if let Some((i, _)) = text.char_indices().rev().nth(5) {
144 return &text[i..];
145 }
146 text
147 }
148}
149
150impl TryFrom<Vec<u8>> for Payload {
151 type Error = ErrorKind;
152
153 fn try_from(vec: Vec<u8>) -> Result<Self> {
154 match serde_json::from_slice(&vec)? {
155 serde_json::Value::String(s) => Ok(Payload::String(s)),
156 value => Ok(Payload::Struct(serde_json::from_value(value)?)),
157 }
158 }
159}
160impl TryInto<Vec<u8>> for Payload {
161 type Error = ErrorKind;
162
163 fn try_into(self) -> Result<Vec<u8>> {
164 match self {
165 Payload::Struct(s) => Ok(serde_json::to_vec(&s)?),
166 Payload::String(s) => Ok(s.as_bytes().to_vec()),
167 }
168 }
169}
170
171impl Truncate for PayloadStruct {
172 fn truncate(&self) -> PayloadStruct {
173 PayloadStruct {
174 dev_id: String::from("...") + Self::truncate_str(&self.dev_id),
175 gw_id: self
176 .gw_id
177 .as_ref()
178 .map(|gwid| String::from("...") + Self::truncate_str(gwid)),
179 t: self.t,
180 dp_id: self.dp_id.clone(),
181 uid: self.uid.clone(),
182 dps: self.dps.clone(),
183 }
184 }
185}
186
187impl Display for PayloadStruct {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 let full_display = std::env::var("TUYA_FULL_DISPLAY").map_or_else(|_| false, |_| true);
190 if full_display {
191 write!(f, "{}", serde_json::to_string(self).unwrap())
192 } else {
193 write!(f, "{}", serde_json::to_string(&self.truncate()).unwrap())
194 }
195 }
196}