1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum ChangeIndication {
18 Ok = 0,
20 Warning = 1,
22 Critical = 2,
24}
25
26impl ChangeIndication {
27 pub fn from_u8(value: u8) -> Option<Self> {
29 match value {
30 0 => Some(ChangeIndication::Ok),
31 1 => Some(ChangeIndication::Warning),
32 2 => Some(ChangeIndication::Critical),
33 _ => None,
34 }
35 }
36
37 pub fn to_u8(self) -> u8 {
39 self as u8
40 }
41}
42
43impl From<ChangeIndication> for u8 {
44 fn from(val: ChangeIndication) -> Self {
45 val as u8
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum DegradationDirection {
52 Up = 0,
54 Down = 1,
56}
57
58impl DegradationDirection {
59 pub fn from_u8(value: u8) -> Option<Self> {
61 match value {
62 0 => Some(DegradationDirection::Up),
63 1 => Some(DegradationDirection::Down),
64 _ => None,
65 }
66 }
67
68 pub fn to_u8(self) -> u8 {
70 self as u8
71 }
72}
73
74impl From<DegradationDirection> for u8 {
75 fn from(val: DegradationDirection) -> Self {
76 val as u8
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
81#[repr(u8)]
82pub enum ProductIdentifierType {
83 Upc = 0,
85 Gtin8 = 1,
87 Ean = 2,
89 Gtin14 = 3,
91 Oem = 4,
93}
94
95impl ProductIdentifierType {
96 pub fn from_u8(value: u8) -> Option<Self> {
98 match value {
99 0 => Some(ProductIdentifierType::Upc),
100 1 => Some(ProductIdentifierType::Gtin8),
101 2 => Some(ProductIdentifierType::Ean),
102 3 => Some(ProductIdentifierType::Gtin14),
103 4 => Some(ProductIdentifierType::Oem),
104 _ => None,
105 }
106 }
107
108 pub fn to_u8(self) -> u8 {
110 self as u8
111 }
112}
113
114impl From<ProductIdentifierType> for u8 {
115 fn from(val: ProductIdentifierType) -> Self {
116 val as u8
117 }
118}
119
120#[derive(Debug, serde::Serialize)]
123pub struct ReplacementProduct {
124 pub product_identifier_type: Option<ProductIdentifierType>,
125 pub product_identifier_value: Option<String>,
126}
127
128pub fn decode_condition(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
134 if let tlv::TlvItemValue::Int(v) = inp {
135 Ok(*v as u8)
136 } else {
137 Err(anyhow::anyhow!("Expected UInt8"))
138 }
139}
140
141pub fn decode_degradation_direction(inp: &tlv::TlvItemValue) -> anyhow::Result<DegradationDirection> {
143 if let tlv::TlvItemValue::Int(v) = inp {
144 DegradationDirection::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
145 } else {
146 Err(anyhow::anyhow!("Expected Integer"))
147 }
148}
149
150pub fn decode_change_indication(inp: &tlv::TlvItemValue) -> anyhow::Result<ChangeIndication> {
152 if let tlv::TlvItemValue::Int(v) = inp {
153 ChangeIndication::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
154 } else {
155 Err(anyhow::anyhow!("Expected Integer"))
156 }
157}
158
159pub fn decode_in_place_indicator(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
161 if let tlv::TlvItemValue::Bool(v) = inp {
162 Ok(*v)
163 } else {
164 Err(anyhow::anyhow!("Expected Bool"))
165 }
166}
167
168pub fn decode_last_changed_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
170 if let tlv::TlvItemValue::Int(v) = inp {
171 Ok(Some(*v))
172 } else {
173 Ok(None)
174 }
175}
176
177pub fn decode_replacement_product_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ReplacementProduct>> {
179 let mut res = Vec::new();
180 if let tlv::TlvItemValue::List(v) = inp {
181 for item in v {
182 res.push(ReplacementProduct {
183 product_identifier_type: item.get_int(&[0]).and_then(|v| ProductIdentifierType::from_u8(v as u8)),
184 product_identifier_value: item.get_string_owned(&[1]),
185 });
186 }
187 }
188 Ok(res)
189}
190
191
192pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
204 if cluster_id != 0x0000 {
206 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0000, got {}\"}}", cluster_id);
207 }
208
209 match attribute_id {
210 0x0000 => {
211 match decode_condition(tlv_value) {
212 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
213 Err(e) => format!("{{\"error\": \"{}\"}}", e),
214 }
215 }
216 0x0001 => {
217 match decode_degradation_direction(tlv_value) {
218 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
219 Err(e) => format!("{{\"error\": \"{}\"}}", e),
220 }
221 }
222 0x0002 => {
223 match decode_change_indication(tlv_value) {
224 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
225 Err(e) => format!("{{\"error\": \"{}\"}}", e),
226 }
227 }
228 0x0003 => {
229 match decode_in_place_indicator(tlv_value) {
230 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
231 Err(e) => format!("{{\"error\": \"{}\"}}", e),
232 }
233 }
234 0x0004 => {
235 match decode_last_changed_time(tlv_value) {
236 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
237 Err(e) => format!("{{\"error\": \"{}\"}}", e),
238 }
239 }
240 0x0005 => {
241 match decode_replacement_product_list(tlv_value) {
242 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
243 Err(e) => format!("{{\"error\": \"{}\"}}", e),
244 }
245 }
246 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
247 }
248}
249
250pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
255 vec![
256 (0x0000, "Condition"),
257 (0x0001, "DegradationDirection"),
258 (0x0002, "ChangeIndication"),
259 (0x0003, "InPlaceIndicator"),
260 (0x0004, "LastChangedTime"),
261 (0x0005, "ReplacementProductList"),
262 ]
263}
264
265pub fn get_command_list() -> Vec<(u32, &'static str)> {
268 vec![
269 (0x00, "ResetCondition"),
270 ]
271}
272
273pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
274 match cmd_id {
275 0x00 => Some("ResetCondition"),
276 _ => None,
277 }
278}
279
280pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
281 match cmd_id {
282 0x00 => Some(vec![]),
283 _ => None,
284 }
285}
286
287pub fn encode_command_json(cmd_id: u32, _args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
288 match cmd_id {
289 0x00 => Ok(vec![]),
290 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
291 }
292}
293
294pub async fn reset_condition(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
298 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_CMD_ID_RESETCONDITION, &[]).await?;
299 Ok(())
300}
301
302pub async fn read_condition(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
304 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_ATTR_ID_CONDITION).await?;
305 decode_condition(&tlv)
306}
307
308pub async fn read_degradation_direction(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<DegradationDirection> {
310 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_ATTR_ID_DEGRADATIONDIRECTION).await?;
311 decode_degradation_direction(&tlv)
312}
313
314pub async fn read_change_indication(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ChangeIndication> {
316 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_ATTR_ID_CHANGEINDICATION).await?;
317 decode_change_indication(&tlv)
318}
319
320pub async fn read_in_place_indicator(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<bool> {
322 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_ATTR_ID_INPLACEINDICATOR).await?;
323 decode_in_place_indicator(&tlv)
324}
325
326pub async fn read_last_changed_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
328 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_ATTR_ID_LASTCHANGEDTIME).await?;
329 decode_last_changed_time(&tlv)
330}
331
332pub async fn read_replacement_product_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ReplacementProduct>> {
334 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_WATER_TANK_LEVEL_MONITORING, crate::clusters::defs::CLUSTER_WATER_TANK_LEVEL_MONITORING_ATTR_ID_REPLACEMENTPRODUCTLIST).await?;
335 decode_replacement_product_list(&tlv)
336}
337