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 StatusCode {
18 Failureduetofault = 2,
20}
21
22impl StatusCode {
23 pub fn from_u8(value: u8) -> Option<Self> {
25 match value {
26 2 => Some(StatusCode::Failureduetofault),
27 _ => None,
28 }
29 }
30
31 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 Closed = 0,
48 Open = 1,
50 Transitioning = 2,
52}
53
54impl ValveState {
55 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 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
77pub type ValveFault = u8;
81
82pub mod valvefault {
84 pub const GENERAL_FAULT: u8 = 0x01;
86 pub const BLOCKED: u8 = 0x02;
88 pub const LEAKING: u8 = 0x04;
90 pub const NOT_CONNECTED: u8 = 0x08;
92 pub const SHORT_CIRCUIT: u8 = 0x10;
94 pub const CURRENT_EXCEEDED: u8 = 0x20;
96}
97
98pub 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
112pub 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
123pub 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
132pub 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
141pub 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
150pub 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
159pub 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
168pub 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
177pub 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
186pub 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
195pub 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
204pub 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
214pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
226 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
302pub 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
322pub 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
362pub 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
370pub 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
376pub 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
382pub 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
388pub 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
394pub 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
400pub 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
406pub 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
412pub 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
418pub 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
424pub 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
430pub 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
436pub 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
453pub 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
468pub 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