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 ErrorStateEnum {
18 Noerror = 0,
20 Unabletostartorresume = 1,
22 Unabletocompleteoperation = 2,
24 Commandinvalidinstate = 3,
26}
27
28impl ErrorStateEnum {
29 pub fn from_u8(value: u8) -> Option<Self> {
31 match value {
32 0 => Some(ErrorStateEnum::Noerror),
33 1 => Some(ErrorStateEnum::Unabletostartorresume),
34 2 => Some(ErrorStateEnum::Unabletocompleteoperation),
35 3 => Some(ErrorStateEnum::Commandinvalidinstate),
36 _ => None,
37 }
38 }
39
40 pub fn to_u8(self) -> u8 {
42 self as u8
43 }
44}
45
46impl From<ErrorStateEnum> for u8 {
47 fn from(val: ErrorStateEnum) -> Self {
48 val as u8
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53#[repr(u8)]
54pub enum OperationalStateEnum {
55 Stopped = 0,
57 Running = 1,
59 Paused = 2,
61 Error = 3,
63}
64
65impl OperationalStateEnum {
66 pub fn from_u8(value: u8) -> Option<Self> {
68 match value {
69 0 => Some(OperationalStateEnum::Stopped),
70 1 => Some(OperationalStateEnum::Running),
71 2 => Some(OperationalStateEnum::Paused),
72 3 => Some(OperationalStateEnum::Error),
73 _ => None,
74 }
75 }
76
77 pub fn to_u8(self) -> u8 {
79 self as u8
80 }
81}
82
83impl From<OperationalStateEnum> for u8 {
84 fn from(val: OperationalStateEnum) -> Self {
85 val as u8
86 }
87}
88
89#[derive(Debug, serde::Serialize)]
92pub struct ErrorState {
93 pub error_state_id: Option<ErrorStateEnum>,
94 pub error_state_label: Option<String>,
95 pub error_state_details: Option<String>,
96}
97
98#[derive(Debug, serde::Serialize)]
99pub struct OperationalState {
100 pub operational_state_id: Option<OperationalStateEnum>,
101 pub operational_state_label: Option<String>,
102}
103
104pub fn decode_phase_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<String>> {
110 let mut res = Vec::new();
111 if let tlv::TlvItemValue::List(v) = inp {
112 for item in v {
113 if let tlv::TlvItemValue::String(s) = &item.value {
114 res.push(s.clone());
115 }
116 }
117 }
118 Ok(res)
119}
120
121pub fn decode_current_phase(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
123 if let tlv::TlvItemValue::Int(v) = inp {
124 Ok(Some(*v as u8))
125 } else {
126 Ok(None)
127 }
128}
129
130pub fn decode_countdown_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
132 if let tlv::TlvItemValue::Int(v) = inp {
133 Ok(Some(*v as u32))
134 } else {
135 Ok(None)
136 }
137}
138
139pub fn decode_operational_state_list(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<OperationalState>> {
141 let mut res = Vec::new();
142 if let tlv::TlvItemValue::List(v) = inp {
143 for item in v {
144 res.push(OperationalState {
145 operational_state_id: item.get_int(&[0]).and_then(|v| OperationalStateEnum::from_u8(v as u8)),
146 operational_state_label: item.get_string_owned(&[1]),
147 });
148 }
149 }
150 Ok(res)
151}
152
153pub fn decode_operational_state(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationalStateEnum> {
155 if let tlv::TlvItemValue::Int(v) = inp {
156 OperationalStateEnum::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
157 } else {
158 Err(anyhow::anyhow!("Expected Integer"))
159 }
160}
161
162pub fn decode_operational_error(inp: &tlv::TlvItemValue) -> anyhow::Result<ErrorState> {
164 if let tlv::TlvItemValue::List(_fields) = inp {
165 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
167 Ok(ErrorState {
168 error_state_id: item.get_int(&[0]).and_then(|v| ErrorStateEnum::from_u8(v as u8)),
169 error_state_label: item.get_string_owned(&[1]),
170 error_state_details: item.get_string_owned(&[2]),
171 })
172 } else {
173 Err(anyhow::anyhow!("Expected struct fields"))
174 }
175}
176
177
178pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
190 if cluster_id != 0x0060 {
192 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0060, got {}\"}}", cluster_id);
193 }
194
195 match attribute_id {
196 0x0000 => {
197 match decode_phase_list(tlv_value) {
198 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
199 Err(e) => format!("{{\"error\": \"{}\"}}", e),
200 }
201 }
202 0x0001 => {
203 match decode_current_phase(tlv_value) {
204 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
205 Err(e) => format!("{{\"error\": \"{}\"}}", e),
206 }
207 }
208 0x0002 => {
209 match decode_countdown_time(tlv_value) {
210 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
211 Err(e) => format!("{{\"error\": \"{}\"}}", e),
212 }
213 }
214 0x0003 => {
215 match decode_operational_state_list(tlv_value) {
216 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
217 Err(e) => format!("{{\"error\": \"{}\"}}", e),
218 }
219 }
220 0x0004 => {
221 match decode_operational_state(tlv_value) {
222 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
223 Err(e) => format!("{{\"error\": \"{}\"}}", e),
224 }
225 }
226 0x0005 => {
227 match decode_operational_error(tlv_value) {
228 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
229 Err(e) => format!("{{\"error\": \"{}\"}}", e),
230 }
231 }
232 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
233 }
234}
235
236pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
241 vec![
242 (0x0000, "PhaseList"),
243 (0x0001, "CurrentPhase"),
244 (0x0002, "CountdownTime"),
245 (0x0003, "OperationalStateList"),
246 (0x0004, "OperationalState"),
247 (0x0005, "OperationalError"),
248 ]
249}
250
251pub fn get_command_list() -> Vec<(u32, &'static str)> {
254 vec![
255 (0x00, "Pause"),
256 (0x01, "Stop"),
257 (0x02, "Start"),
258 (0x03, "Resume"),
259 ]
260}
261
262pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
263 match cmd_id {
264 0x00 => Some("Pause"),
265 0x01 => Some("Stop"),
266 0x02 => Some("Start"),
267 0x03 => Some("Resume"),
268 _ => None,
269 }
270}
271
272pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
273 match cmd_id {
274 0x00 => Some(vec![]),
275 0x01 => Some(vec![]),
276 0x02 => Some(vec![]),
277 0x03 => Some(vec![]),
278 _ => None,
279 }
280}
281
282pub fn encode_command_json(cmd_id: u32, _args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
283 match cmd_id {
284 0x00 => Ok(vec![]),
285 0x01 => Ok(vec![]),
286 0x02 => Ok(vec![]),
287 0x03 => Ok(vec![]),
288 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
289 }
290}
291
292#[derive(Debug, serde::Serialize)]
293pub struct OperationalCommandResponse {
294 pub command_response_state: Option<ErrorState>,
295}
296
297pub fn decode_operational_command_response(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationalCommandResponse> {
301 if let tlv::TlvItemValue::List(_fields) = inp {
302 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
303 Ok(OperationalCommandResponse {
304 command_response_state: {
305 if let Some(nested_tlv) = item.get(&[0]) {
306 if let tlv::TlvItemValue::List(_) = nested_tlv {
307 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
308 Some(ErrorState {
309 error_state_id: nested_item.get_int(&[0]).and_then(|v| ErrorStateEnum::from_u8(v as u8)),
310 error_state_label: nested_item.get_string_owned(&[1]),
311 error_state_details: nested_item.get_string_owned(&[2]),
312 })
313 } else {
314 None
315 }
316 } else {
317 None
318 }
319 },
320 })
321 } else {
322 Err(anyhow::anyhow!("Expected struct fields"))
323 }
324}
325
326pub async fn pause(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperationalCommandResponse> {
330 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_CMD_ID_PAUSE, &[]).await?;
331 decode_operational_command_response(&tlv)
332}
333
334pub async fn stop(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperationalCommandResponse> {
336 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_CMD_ID_STOP, &[]).await?;
337 decode_operational_command_response(&tlv)
338}
339
340pub async fn start(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperationalCommandResponse> {
342 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_CMD_ID_START, &[]).await?;
343 decode_operational_command_response(&tlv)
344}
345
346pub async fn resume(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperationalCommandResponse> {
348 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_CMD_ID_RESUME, &[]).await?;
349 decode_operational_command_response(&tlv)
350}
351
352pub async fn read_phase_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<String>> {
354 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_ATTR_ID_PHASELIST).await?;
355 decode_phase_list(&tlv)
356}
357
358pub async fn read_current_phase(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
360 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_ATTR_ID_CURRENTPHASE).await?;
361 decode_current_phase(&tlv)
362}
363
364pub async fn read_countdown_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
366 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_ATTR_ID_COUNTDOWNTIME).await?;
367 decode_countdown_time(&tlv)
368}
369
370pub async fn read_operational_state_list(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<OperationalState>> {
372 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_ATTR_ID_OPERATIONALSTATELIST).await?;
373 decode_operational_state_list(&tlv)
374}
375
376pub async fn read_operational_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<OperationalStateEnum> {
378 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_ATTR_ID_OPERATIONALSTATE).await?;
379 decode_operational_state(&tlv)
380}
381
382pub async fn read_operational_error(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ErrorState> {
384 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_OPERATIONAL_STATE, crate::clusters::defs::CLUSTER_OPERATIONAL_STATE_ATTR_ID_OPERATIONALERROR).await?;
385 decode_operational_error(&tlv)
386}
387
388#[derive(Debug, serde::Serialize)]
389pub struct OperationalErrorEvent {
390 pub error_state: Option<ErrorState>,
391}
392
393#[derive(Debug, serde::Serialize)]
394pub struct OperationCompletionEvent {
395 pub completion_error_code: Option<u8>,
396 pub total_operational_time: Option<u32>,
397 pub paused_time: Option<u32>,
398}
399
400pub fn decode_operational_error_event(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationalErrorEvent> {
404 if let tlv::TlvItemValue::List(_fields) = inp {
405 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
406 Ok(OperationalErrorEvent {
407 error_state: {
408 if let Some(nested_tlv) = item.get(&[0]) {
409 if let tlv::TlvItemValue::List(_) = nested_tlv {
410 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
411 Some(ErrorState {
412 error_state_id: nested_item.get_int(&[0]).and_then(|v| ErrorStateEnum::from_u8(v as u8)),
413 error_state_label: nested_item.get_string_owned(&[1]),
414 error_state_details: nested_item.get_string_owned(&[2]),
415 })
416 } else {
417 None
418 }
419 } else {
420 None
421 }
422 },
423 })
424 } else {
425 Err(anyhow::anyhow!("Expected struct fields"))
426 }
427}
428
429pub fn decode_operation_completion_event(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationCompletionEvent> {
431 if let tlv::TlvItemValue::List(_fields) = inp {
432 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
433 Ok(OperationCompletionEvent {
434 completion_error_code: item.get_int(&[0]).map(|v| v as u8),
435 total_operational_time: item.get_int(&[1]).map(|v| v as u32),
436 paused_time: item.get_int(&[2]).map(|v| v as u32),
437 })
438 } else {
439 Err(anyhow::anyhow!("Expected struct fields"))
440 }
441}
442