1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum ControlMode {
16 Constantspeed = 0,
18 Constantpressure = 1,
20 Proportionalpressure = 2,
22 Constantflow = 3,
24 Constanttemperature = 5,
26 Automatic = 7,
28}
29
30impl ControlMode {
31 pub fn from_u8(value: u8) -> Option<Self> {
33 match value {
34 0 => Some(ControlMode::Constantspeed),
35 1 => Some(ControlMode::Constantpressure),
36 2 => Some(ControlMode::Proportionalpressure),
37 3 => Some(ControlMode::Constantflow),
38 5 => Some(ControlMode::Constanttemperature),
39 7 => Some(ControlMode::Automatic),
40 _ => None,
41 }
42 }
43
44 pub fn to_u8(self) -> u8 {
46 self as u8
47 }
48}
49
50impl From<ControlMode> for u8 {
51 fn from(val: ControlMode) -> Self {
52 val as u8
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
57#[repr(u8)]
58pub enum OperationMode {
59 Normal = 0,
61 Minimum = 1,
63 Maximum = 2,
65 Local = 3,
67}
68
69impl OperationMode {
70 pub fn from_u8(value: u8) -> Option<Self> {
72 match value {
73 0 => Some(OperationMode::Normal),
74 1 => Some(OperationMode::Minimum),
75 2 => Some(OperationMode::Maximum),
76 3 => Some(OperationMode::Local),
77 _ => None,
78 }
79 }
80
81 pub fn to_u8(self) -> u8 {
83 self as u8
84 }
85}
86
87impl From<OperationMode> for u8 {
88 fn from(val: OperationMode) -> Self {
89 val as u8
90 }
91}
92
93pub type PumpStatus = u16;
97
98pub mod pumpstatus {
100 pub const DEVICE_FAULT: u16 = 0x01;
102 pub const SUPPLY_FAULT: u16 = 0x02;
104 pub const SPEED_LOW: u16 = 0x04;
106 pub const SPEED_HIGH: u16 = 0x08;
108 pub const LOCAL_OVERRIDE: u16 = 0x10;
110 pub const RUNNING: u16 = 0x20;
112 pub const REMOTE_PRESSURE: u16 = 0x40;
114 pub const REMOTE_FLOW: u16 = 0x80;
116 pub const REMOTE_TEMPERATURE: u16 = 0x100;
118}
119
120pub fn decode_max_pressure(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
124 if let tlv::TlvItemValue::Int(v) = inp {
125 Ok(Some(*v as i16))
126 } else {
127 Ok(None)
128 }
129}
130
131pub fn decode_max_speed(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
133 if let tlv::TlvItemValue::Int(v) = inp {
134 Ok(Some(*v as u16))
135 } else {
136 Ok(None)
137 }
138}
139
140pub fn decode_max_flow(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
142 if let tlv::TlvItemValue::Int(v) = inp {
143 Ok(Some(*v as u16))
144 } else {
145 Ok(None)
146 }
147}
148
149pub fn decode_min_const_pressure(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
151 if let tlv::TlvItemValue::Int(v) = inp {
152 Ok(Some(*v as i16))
153 } else {
154 Ok(None)
155 }
156}
157
158pub fn decode_max_const_pressure(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
160 if let tlv::TlvItemValue::Int(v) = inp {
161 Ok(Some(*v as i16))
162 } else {
163 Ok(None)
164 }
165}
166
167pub fn decode_min_comp_pressure(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
169 if let tlv::TlvItemValue::Int(v) = inp {
170 Ok(Some(*v as i16))
171 } else {
172 Ok(None)
173 }
174}
175
176pub fn decode_max_comp_pressure(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
178 if let tlv::TlvItemValue::Int(v) = inp {
179 Ok(Some(*v as i16))
180 } else {
181 Ok(None)
182 }
183}
184
185pub fn decode_min_const_speed(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
187 if let tlv::TlvItemValue::Int(v) = inp {
188 Ok(Some(*v as u16))
189 } else {
190 Ok(None)
191 }
192}
193
194pub fn decode_max_const_speed(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
196 if let tlv::TlvItemValue::Int(v) = inp {
197 Ok(Some(*v as u16))
198 } else {
199 Ok(None)
200 }
201}
202
203pub fn decode_min_const_flow(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
205 if let tlv::TlvItemValue::Int(v) = inp {
206 Ok(Some(*v as u16))
207 } else {
208 Ok(None)
209 }
210}
211
212pub fn decode_max_const_flow(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
214 if let tlv::TlvItemValue::Int(v) = inp {
215 Ok(Some(*v as u16))
216 } else {
217 Ok(None)
218 }
219}
220
221pub fn decode_min_const_temp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
223 if let tlv::TlvItemValue::Int(v) = inp {
224 Ok(Some(*v as i16))
225 } else {
226 Ok(None)
227 }
228}
229
230pub fn decode_max_const_temp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
232 if let tlv::TlvItemValue::Int(v) = inp {
233 Ok(Some(*v as i16))
234 } else {
235 Ok(None)
236 }
237}
238
239pub fn decode_pump_status(inp: &tlv::TlvItemValue) -> anyhow::Result<PumpStatus> {
241 if let tlv::TlvItemValue::Int(v) = inp {
242 Ok(*v as u16)
243 } else {
244 Err(anyhow::anyhow!("Expected Integer"))
245 }
246}
247
248pub fn decode_effective_operation_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationMode> {
250 if let tlv::TlvItemValue::Int(v) = inp {
251 OperationMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
252 } else {
253 Err(anyhow::anyhow!("Expected Integer"))
254 }
255}
256
257pub fn decode_effective_control_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ControlMode> {
259 if let tlv::TlvItemValue::Int(v) = inp {
260 ControlMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
261 } else {
262 Err(anyhow::anyhow!("Expected Integer"))
263 }
264}
265
266pub fn decode_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
268 if let tlv::TlvItemValue::Int(v) = inp {
269 Ok(Some(*v as i16))
270 } else {
271 Ok(None)
272 }
273}
274
275pub fn decode_speed(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
277 if let tlv::TlvItemValue::Int(v) = inp {
278 Ok(Some(*v as u16))
279 } else {
280 Ok(None)
281 }
282}
283
284pub fn decode_lifetime_running_hours(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
286 if let tlv::TlvItemValue::Int(v) = inp {
287 Ok(Some(*v as u8))
288 } else {
289 Ok(None)
290 }
291}
292
293pub fn decode_power(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
295 if let tlv::TlvItemValue::Int(v) = inp {
296 Ok(Some(*v as u8))
297 } else {
298 Ok(None)
299 }
300}
301
302pub fn decode_lifetime_energy_consumed(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
304 if let tlv::TlvItemValue::Int(v) = inp {
305 Ok(Some(*v as u32))
306 } else {
307 Ok(None)
308 }
309}
310
311pub fn decode_operation_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<OperationMode> {
313 if let tlv::TlvItemValue::Int(v) = inp {
314 OperationMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
315 } else {
316 Err(anyhow::anyhow!("Expected Integer"))
317 }
318}
319
320pub fn decode_control_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ControlMode> {
322 if let tlv::TlvItemValue::Int(v) = inp {
323 ControlMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
324 } else {
325 Err(anyhow::anyhow!("Expected Integer"))
326 }
327}
328
329pub fn decode_alarm_mask(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
331 if let tlv::TlvItemValue::Int(v) = inp {
332 Ok(*v as u8)
333 } else {
334 Err(anyhow::anyhow!("Expected UInt8"))
335 }
336}
337
338
339pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
351 if cluster_id != 0x0200 {
353 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0200, got {}\"}}", cluster_id);
354 }
355
356 match attribute_id {
357 0x0000 => {
358 match decode_max_pressure(tlv_value) {
359 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
360 Err(e) => format!("{{\"error\": \"{}\"}}", e),
361 }
362 }
363 0x0001 => {
364 match decode_max_speed(tlv_value) {
365 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
366 Err(e) => format!("{{\"error\": \"{}\"}}", e),
367 }
368 }
369 0x0002 => {
370 match decode_max_flow(tlv_value) {
371 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
372 Err(e) => format!("{{\"error\": \"{}\"}}", e),
373 }
374 }
375 0x0003 => {
376 match decode_min_const_pressure(tlv_value) {
377 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
378 Err(e) => format!("{{\"error\": \"{}\"}}", e),
379 }
380 }
381 0x0004 => {
382 match decode_max_const_pressure(tlv_value) {
383 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
384 Err(e) => format!("{{\"error\": \"{}\"}}", e),
385 }
386 }
387 0x0005 => {
388 match decode_min_comp_pressure(tlv_value) {
389 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
390 Err(e) => format!("{{\"error\": \"{}\"}}", e),
391 }
392 }
393 0x0006 => {
394 match decode_max_comp_pressure(tlv_value) {
395 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
396 Err(e) => format!("{{\"error\": \"{}\"}}", e),
397 }
398 }
399 0x0007 => {
400 match decode_min_const_speed(tlv_value) {
401 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
402 Err(e) => format!("{{\"error\": \"{}\"}}", e),
403 }
404 }
405 0x0008 => {
406 match decode_max_const_speed(tlv_value) {
407 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
408 Err(e) => format!("{{\"error\": \"{}\"}}", e),
409 }
410 }
411 0x0009 => {
412 match decode_min_const_flow(tlv_value) {
413 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
414 Err(e) => format!("{{\"error\": \"{}\"}}", e),
415 }
416 }
417 0x000A => {
418 match decode_max_const_flow(tlv_value) {
419 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
420 Err(e) => format!("{{\"error\": \"{}\"}}", e),
421 }
422 }
423 0x000B => {
424 match decode_min_const_temp(tlv_value) {
425 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
426 Err(e) => format!("{{\"error\": \"{}\"}}", e),
427 }
428 }
429 0x000C => {
430 match decode_max_const_temp(tlv_value) {
431 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
432 Err(e) => format!("{{\"error\": \"{}\"}}", e),
433 }
434 }
435 0x0010 => {
436 match decode_pump_status(tlv_value) {
437 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
438 Err(e) => format!("{{\"error\": \"{}\"}}", e),
439 }
440 }
441 0x0011 => {
442 match decode_effective_operation_mode(tlv_value) {
443 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
444 Err(e) => format!("{{\"error\": \"{}\"}}", e),
445 }
446 }
447 0x0012 => {
448 match decode_effective_control_mode(tlv_value) {
449 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
450 Err(e) => format!("{{\"error\": \"{}\"}}", e),
451 }
452 }
453 0x0013 => {
454 match decode_capacity(tlv_value) {
455 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
456 Err(e) => format!("{{\"error\": \"{}\"}}", e),
457 }
458 }
459 0x0014 => {
460 match decode_speed(tlv_value) {
461 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
462 Err(e) => format!("{{\"error\": \"{}\"}}", e),
463 }
464 }
465 0x0015 => {
466 match decode_lifetime_running_hours(tlv_value) {
467 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
468 Err(e) => format!("{{\"error\": \"{}\"}}", e),
469 }
470 }
471 0x0016 => {
472 match decode_power(tlv_value) {
473 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
474 Err(e) => format!("{{\"error\": \"{}\"}}", e),
475 }
476 }
477 0x0017 => {
478 match decode_lifetime_energy_consumed(tlv_value) {
479 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
480 Err(e) => format!("{{\"error\": \"{}\"}}", e),
481 }
482 }
483 0x0020 => {
484 match decode_operation_mode(tlv_value) {
485 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
486 Err(e) => format!("{{\"error\": \"{}\"}}", e),
487 }
488 }
489 0x0021 => {
490 match decode_control_mode(tlv_value) {
491 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
492 Err(e) => format!("{{\"error\": \"{}\"}}", e),
493 }
494 }
495 0x0022 => {
496 match decode_alarm_mask(tlv_value) {
497 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
498 Err(e) => format!("{{\"error\": \"{}\"}}", e),
499 }
500 }
501 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
502 }
503}
504
505pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
510 vec![
511 (0x0000, "MaxPressure"),
512 (0x0001, "MaxSpeed"),
513 (0x0002, "MaxFlow"),
514 (0x0003, "MinConstPressure"),
515 (0x0004, "MaxConstPressure"),
516 (0x0005, "MinCompPressure"),
517 (0x0006, "MaxCompPressure"),
518 (0x0007, "MinConstSpeed"),
519 (0x0008, "MaxConstSpeed"),
520 (0x0009, "MinConstFlow"),
521 (0x000A, "MaxConstFlow"),
522 (0x000B, "MinConstTemp"),
523 (0x000C, "MaxConstTemp"),
524 (0x0010, "PumpStatus"),
525 (0x0011, "EffectiveOperationMode"),
526 (0x0012, "EffectiveControlMode"),
527 (0x0013, "Capacity"),
528 (0x0014, "Speed"),
529 (0x0015, "LifetimeRunningHours"),
530 (0x0016, "Power"),
531 (0x0017, "LifetimeEnergyConsumed"),
532 (0x0020, "OperationMode"),
533 (0x0021, "ControlMode"),
534 (0x0022, "AlarmMask"),
535 ]
536}
537