matc/clusters/codec/
resource_monitoring.rs

1//! Matter TLV encoders and decoders for Resource Monitoring Clusters
2//! Cluster ID: 0x0000
3//!
4//! This file is automatically generated from ResourceMonitoring.xml
5
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Enum definitions
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum ChangeIndication {
18    /// Resource is in good condition, no intervention required
19    Ok = 0,
20    /// Resource will be exhausted soon, intervention will shortly be required
21    Warning = 1,
22    /// Resource is exhausted, immediate intervention is required
23    Critical = 2,
24}
25
26impl ChangeIndication {
27    /// Convert from u8 value
28    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    /// Convert to u8 value
38    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    /// The degradation of the resource is indicated by an upwards moving/increasing value
53    Up = 0,
54    /// The degradation of the resource is indicated by a downwards moving/decreasing value
55    Down = 1,
56}
57
58impl DegradationDirection {
59    /// Convert from u8 value
60    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    /// Convert to u8 value
69    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    /// 12-digit Universal Product Code
84    Upc = 0,
85    /// 8-digit Global Trade Item Number
86    Gtin8 = 1,
87    /// 13-digit European Article Number
88    Ean = 2,
89    /// 14-digit Global Trade Item Number
90    Gtin14 = 3,
91    /// Original Equipment Manufacturer part number
92    Oem = 4,
93}
94
95impl ProductIdentifierType {
96    /// Convert from u8 value
97    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    /// Convert to u8 value
109    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// Struct definitions
121
122#[derive(Debug, serde::Serialize)]
123pub struct ReplacementProduct {
124    pub product_identifier_type: Option<ProductIdentifierType>,
125    pub product_identifier_value: Option<String>,
126}
127
128// Command encoders
129
130// Attribute decoders
131
132/// Decode Condition attribute (0x0000)
133pub 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
141/// Decode DegradationDirection attribute (0x0001)
142pub 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
150/// Decode ChangeIndication attribute (0x0002)
151pub 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
159/// Decode InPlaceIndicator attribute (0x0003)
160pub 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
168/// Decode LastChangedTime attribute (0x0004)
169pub 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
177/// Decode ReplacementProductList attribute (0x0005)
178pub 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
192// JSON dispatcher function
193
194/// Decode attribute value and return as JSON string
195///
196/// # Parameters
197/// * `cluster_id` - The cluster identifier
198/// * `attribute_id` - The attribute identifier
199/// * `tlv_value` - The TLV value to decode
200///
201/// # Returns
202/// JSON string representation of the decoded value or error
203pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
204    // Verify this is the correct cluster
205    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
250/// Get list of all attributes supported by this cluster
251///
252/// # Returns
253/// Vector of tuples containing (attribute_id, attribute_name)
254pub 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
265// Command listing
266
267pub 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
294// Typed facade (invokes + reads)
295
296/// Invoke `ResetCondition` command on cluster `Water Tank Level Monitoring`.
297pub 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
302/// Read `Condition` attribute from cluster `Water Tank Level Monitoring`.
303pub 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
308/// Read `DegradationDirection` attribute from cluster `Water Tank Level Monitoring`.
309pub 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
314/// Read `ChangeIndication` attribute from cluster `Water Tank Level Monitoring`.
315pub 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
320/// Read `InPlaceIndicator` attribute from cluster `Water Tank Level Monitoring`.
321pub 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
326/// Read `LastChangedTime` attribute from cluster `Water Tank Level Monitoring`.
327pub 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
332/// Read `ReplacementProductList` attribute from cluster `Water Tank Level Monitoring`.
333pub 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