matc/clusters/codec/
valve_configuration_control.rs

1//! Matter TLV encoders and decoders for Valve Configuration and Control Cluster
2//! Cluster ID: 0x0081
3//!
4//! This file is automatically generated from ValveConfigurationControl.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 StatusCode {
18    /// The requested action could not be performed due to a fault on the valve.
19    Failureduetofault = 2,
20}
21
22impl StatusCode {
23    /// Convert from u8 value
24    pub fn from_u8(value: u8) -> Option<Self> {
25        match value {
26            2 => Some(StatusCode::Failureduetofault),
27            _ => None,
28        }
29    }
30
31    /// Convert to u8 value
32    pub fn to_u8(self) -> u8 {
33        self as u8
34    }
35}
36
37impl From<StatusCode> for u8 {
38    fn from(val: StatusCode) -> Self {
39        val as u8
40    }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
44#[repr(u8)]
45pub enum ValveState {
46    /// Valve is in closed position
47    Closed = 0,
48    /// Valve is in open position
49    Open = 1,
50    /// Valve is transitioning between closed and open positions or between levels
51    Transitioning = 2,
52}
53
54impl ValveState {
55    /// Convert from u8 value
56    pub fn from_u8(value: u8) -> Option<Self> {
57        match value {
58            0 => Some(ValveState::Closed),
59            1 => Some(ValveState::Open),
60            2 => Some(ValveState::Transitioning),
61            _ => None,
62        }
63    }
64
65    /// Convert to u8 value
66    pub fn to_u8(self) -> u8 {
67        self as u8
68    }
69}
70
71impl From<ValveState> for u8 {
72    fn from(val: ValveState) -> Self {
73        val as u8
74    }
75}
76
77// Bitmap definitions
78
79/// ValveFault bitmap type
80pub type ValveFault = u8;
81
82/// Constants for ValveFault
83pub mod valvefault {
84    /// Unspecified fault detected
85    pub const GENERAL_FAULT: u8 = 0x01;
86    /// Valve is blocked
87    pub const BLOCKED: u8 = 0x02;
88    /// Valve has detected a leak
89    pub const LEAKING: u8 = 0x04;
90    /// No valve is connected to controller
91    pub const NOT_CONNECTED: u8 = 0x08;
92    /// Short circuit is detected
93    pub const SHORT_CIRCUIT: u8 = 0x10;
94    /// The available current has been exceeded
95    pub const CURRENT_EXCEEDED: u8 = 0x20;
96}
97
98// Command encoders
99
100/// Encode Open command (0x00)
101pub fn encode_open(open_duration: Option<u32>, target_level: u8) -> anyhow::Result<Vec<u8>> {
102    let tlv = tlv::TlvItemEnc {
103        tag: 0,
104        value: tlv::TlvItemValueEnc::StructInvisible(vec![
105        (0, tlv::TlvItemValueEnc::UInt32(open_duration.unwrap_or(0))).into(),
106        (1, tlv::TlvItemValueEnc::UInt8(target_level)).into(),
107        ]),
108    };
109    Ok(tlv.encode()?)
110}
111
112// Attribute decoders
113
114/// Decode OpenDuration attribute (0x0000)
115pub fn decode_open_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
116    if let tlv::TlvItemValue::Int(v) = inp {
117        Ok(Some(*v as u32))
118    } else {
119        Ok(None)
120    }
121}
122
123/// Decode DefaultOpenDuration attribute (0x0001)
124pub fn decode_default_open_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
125    if let tlv::TlvItemValue::Int(v) = inp {
126        Ok(Some(*v as u32))
127    } else {
128        Ok(None)
129    }
130}
131
132/// Decode AutoCloseTime attribute (0x0002)
133pub fn decode_auto_close_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
134    if let tlv::TlvItemValue::Int(v) = inp {
135        Ok(Some(*v))
136    } else {
137        Ok(None)
138    }
139}
140
141/// Decode RemainingDuration attribute (0x0003)
142pub fn decode_remaining_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
143    if let tlv::TlvItemValue::Int(v) = inp {
144        Ok(Some(*v as u32))
145    } else {
146        Ok(None)
147    }
148}
149
150/// Decode CurrentState attribute (0x0004)
151pub fn decode_current_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ValveState>> {
152    if let tlv::TlvItemValue::Int(v) = inp {
153        Ok(ValveState::from_u8(*v as u8))
154    } else {
155        Ok(None)
156    }
157}
158
159/// Decode TargetState attribute (0x0005)
160pub fn decode_target_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ValveState>> {
161    if let tlv::TlvItemValue::Int(v) = inp {
162        Ok(ValveState::from_u8(*v as u8))
163    } else {
164        Ok(None)
165    }
166}
167
168/// Decode CurrentLevel attribute (0x0006)
169pub fn decode_current_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
170    if let tlv::TlvItemValue::Int(v) = inp {
171        Ok(Some(*v as u8))
172    } else {
173        Ok(None)
174    }
175}
176
177/// Decode TargetLevel attribute (0x0007)
178pub fn decode_target_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
179    if let tlv::TlvItemValue::Int(v) = inp {
180        Ok(Some(*v as u8))
181    } else {
182        Ok(None)
183    }
184}
185
186/// Decode DefaultOpenLevel attribute (0x0008)
187pub fn decode_default_open_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
188    if let tlv::TlvItemValue::Int(v) = inp {
189        Ok(*v as u8)
190    } else {
191        Err(anyhow::anyhow!("Expected UInt8"))
192    }
193}
194
195/// Decode ValveFault attribute (0x0009)
196pub fn decode_valve_fault(inp: &tlv::TlvItemValue) -> anyhow::Result<ValveFault> {
197    if let tlv::TlvItemValue::Int(v) = inp {
198        Ok(*v as u8)
199    } else {
200        Err(anyhow::anyhow!("Expected Integer"))
201    }
202}
203
204/// Decode LevelStep attribute (0x000A)
205pub fn decode_level_step(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
206    if let tlv::TlvItemValue::Int(v) = inp {
207        Ok(*v as u8)
208    } else {
209        Err(anyhow::anyhow!("Expected UInt8"))
210    }
211}
212
213
214// JSON dispatcher function
215
216/// Decode attribute value and return as JSON string
217///
218/// # Parameters
219/// * `cluster_id` - The cluster identifier
220/// * `attribute_id` - The attribute identifier
221/// * `tlv_value` - The TLV value to decode
222///
223/// # Returns
224/// JSON string representation of the decoded value or error
225pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
226    // Verify this is the correct cluster
227    if cluster_id != 0x0081 {
228        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0081, got {}\"}}", cluster_id);
229    }
230
231    match attribute_id {
232        0x0000 => {
233            match decode_open_duration(tlv_value) {
234                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
235                Err(e) => format!("{{\"error\": \"{}\"}}", e),
236            }
237        }
238        0x0001 => {
239            match decode_default_open_duration(tlv_value) {
240                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
241                Err(e) => format!("{{\"error\": \"{}\"}}", e),
242            }
243        }
244        0x0002 => {
245            match decode_auto_close_time(tlv_value) {
246                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
247                Err(e) => format!("{{\"error\": \"{}\"}}", e),
248            }
249        }
250        0x0003 => {
251            match decode_remaining_duration(tlv_value) {
252                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
253                Err(e) => format!("{{\"error\": \"{}\"}}", e),
254            }
255        }
256        0x0004 => {
257            match decode_current_state(tlv_value) {
258                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
259                Err(e) => format!("{{\"error\": \"{}\"}}", e),
260            }
261        }
262        0x0005 => {
263            match decode_target_state(tlv_value) {
264                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
265                Err(e) => format!("{{\"error\": \"{}\"}}", e),
266            }
267        }
268        0x0006 => {
269            match decode_current_level(tlv_value) {
270                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
271                Err(e) => format!("{{\"error\": \"{}\"}}", e),
272            }
273        }
274        0x0007 => {
275            match decode_target_level(tlv_value) {
276                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
277                Err(e) => format!("{{\"error\": \"{}\"}}", e),
278            }
279        }
280        0x0008 => {
281            match decode_default_open_level(tlv_value) {
282                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
283                Err(e) => format!("{{\"error\": \"{}\"}}", e),
284            }
285        }
286        0x0009 => {
287            match decode_valve_fault(tlv_value) {
288                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
289                Err(e) => format!("{{\"error\": \"{}\"}}", e),
290            }
291        }
292        0x000A => {
293            match decode_level_step(tlv_value) {
294                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
295                Err(e) => format!("{{\"error\": \"{}\"}}", e),
296            }
297        }
298        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
299    }
300}
301
302/// Get list of all attributes supported by this cluster
303///
304/// # Returns
305/// Vector of tuples containing (attribute_id, attribute_name)
306pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
307    vec![
308        (0x0000, "OpenDuration"),
309        (0x0001, "DefaultOpenDuration"),
310        (0x0002, "AutoCloseTime"),
311        (0x0003, "RemainingDuration"),
312        (0x0004, "CurrentState"),
313        (0x0005, "TargetState"),
314        (0x0006, "CurrentLevel"),
315        (0x0007, "TargetLevel"),
316        (0x0008, "DefaultOpenLevel"),
317        (0x0009, "ValveFault"),
318        (0x000A, "LevelStep"),
319    ]
320}
321
322// Command listing
323
324pub fn get_command_list() -> Vec<(u32, &'static str)> {
325    vec![
326        (0x00, "Open"),
327        (0x01, "Close"),
328    ]
329}
330
331pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
332    match cmd_id {
333        0x00 => Some("Open"),
334        0x01 => Some("Close"),
335        _ => None,
336    }
337}
338
339pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
340    match cmd_id {
341        0x00 => Some(vec![
342            crate::clusters::codec::CommandField { tag: 0, name: "open_duration", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: true },
343            crate::clusters::codec::CommandField { tag: 1, name: "target_level", kind: crate::clusters::codec::FieldKind::U32, optional: true, nullable: false },
344        ]),
345        0x01 => Some(vec![]),
346        _ => None,
347    }
348}
349
350pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
351    match cmd_id {
352        0x00 => {
353        let open_duration = crate::clusters::codec::json_util::get_opt_u32(args, "open_duration")?;
354        let target_level = crate::clusters::codec::json_util::get_u8(args, "target_level")?;
355        encode_open(open_duration, target_level)
356        }
357        0x01 => Ok(vec![]),
358        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
359    }
360}
361
362// Typed facade (invokes + reads)
363
364/// Invoke `Open` command on cluster `Valve Configuration and Control`.
365pub async fn open(conn: &crate::controller::Connection, endpoint: u16, open_duration: Option<u32>, target_level: u8) -> anyhow::Result<()> {
366    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_CMD_ID_OPEN, &encode_open(open_duration, target_level)?).await?;
367    Ok(())
368}
369
370/// Invoke `Close` command on cluster `Valve Configuration and Control`.
371pub async fn close(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
372    conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_CMD_ID_CLOSE, &[]).await?;
373    Ok(())
374}
375
376/// Read `OpenDuration` attribute from cluster `Valve Configuration and Control`.
377pub async fn read_open_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
378    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_OPENDURATION).await?;
379    decode_open_duration(&tlv)
380}
381
382/// Read `DefaultOpenDuration` attribute from cluster `Valve Configuration and Control`.
383pub async fn read_default_open_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
384    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_DEFAULTOPENDURATION).await?;
385    decode_default_open_duration(&tlv)
386}
387
388/// Read `AutoCloseTime` attribute from cluster `Valve Configuration and Control`.
389pub async fn read_auto_close_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
390    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_AUTOCLOSETIME).await?;
391    decode_auto_close_time(&tlv)
392}
393
394/// Read `RemainingDuration` attribute from cluster `Valve Configuration and Control`.
395pub async fn read_remaining_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
396    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_REMAININGDURATION).await?;
397    decode_remaining_duration(&tlv)
398}
399
400/// Read `CurrentState` attribute from cluster `Valve Configuration and Control`.
401pub async fn read_current_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<ValveState>> {
402    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_CURRENTSTATE).await?;
403    decode_current_state(&tlv)
404}
405
406/// Read `TargetState` attribute from cluster `Valve Configuration and Control`.
407pub async fn read_target_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<ValveState>> {
408    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_TARGETSTATE).await?;
409    decode_target_state(&tlv)
410}
411
412/// Read `CurrentLevel` attribute from cluster `Valve Configuration and Control`.
413pub async fn read_current_level(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
414    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_CURRENTLEVEL).await?;
415    decode_current_level(&tlv)
416}
417
418/// Read `TargetLevel` attribute from cluster `Valve Configuration and Control`.
419pub async fn read_target_level(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
420    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_TARGETLEVEL).await?;
421    decode_target_level(&tlv)
422}
423
424/// Read `DefaultOpenLevel` attribute from cluster `Valve Configuration and Control`.
425pub async fn read_default_open_level(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
426    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_DEFAULTOPENLEVEL).await?;
427    decode_default_open_level(&tlv)
428}
429
430/// Read `ValveFault` attribute from cluster `Valve Configuration and Control`.
431pub async fn read_valve_fault(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ValveFault> {
432    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_VALVEFAULT).await?;
433    decode_valve_fault(&tlv)
434}
435
436/// Read `LevelStep` attribute from cluster `Valve Configuration and Control`.
437pub async fn read_level_step(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
438    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_VALVE_CONFIGURATION_AND_CONTROL, crate::clusters::defs::CLUSTER_VALVE_CONFIGURATION_AND_CONTROL_ATTR_ID_LEVELSTEP).await?;
439    decode_level_step(&tlv)
440}
441
442#[derive(Debug, serde::Serialize)]
443pub struct ValveStateChangedEvent {
444    pub valve_state: Option<ValveState>,
445    pub valve_level: Option<u8>,
446}
447
448#[derive(Debug, serde::Serialize)]
449pub struct ValveFaultEvent {
450    pub valve_fault: Option<ValveFault>,
451}
452
453// Event decoders
454
455/// Decode ValveStateChanged event (0x00, priority: info)
456pub fn decode_valve_state_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ValveStateChangedEvent> {
457    if let tlv::TlvItemValue::List(_fields) = inp {
458        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
459        Ok(ValveStateChangedEvent {
460                                valve_state: item.get_int(&[0]).and_then(|v| ValveState::from_u8(v as u8)),
461                                valve_level: item.get_int(&[1]).map(|v| v as u8),
462        })
463    } else {
464        Err(anyhow::anyhow!("Expected struct fields"))
465    }
466}
467
468/// Decode ValveFault event (0x01, priority: info)
469pub fn decode_valve_fault_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ValveFaultEvent> {
470    if let tlv::TlvItemValue::List(_fields) = inp {
471        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
472        Ok(ValveFaultEvent {
473                                valve_fault: item.get_int(&[0]).map(|v| v as u8),
474        })
475    } else {
476        Err(anyhow::anyhow!("Expected struct fields"))
477    }
478}
479