1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct Preset {
15 pub preset_handle: Option<Vec<u8>>,
16 pub preset_scenario: Option<u8>,
17 pub name: Option<String>,
18 pub cooling_setpoint: Option<u8>,
19 pub heating_setpoint: Option<u8>,
20 pub built_in: Option<bool>,
21}
22
23#[derive(Debug, serde::Serialize)]
24pub struct PresetType {
25 pub preset_scenario: Option<u8>,
26 pub number_of_presets: Option<u8>,
27 pub preset_type_features: Option<u8>,
28}
29
30#[derive(Debug, serde::Serialize)]
31pub struct Schedule {
32 pub schedule_handle: Option<Vec<u8>>,
33 pub system_mode: Option<u8>,
34 pub name: Option<String>,
35 pub preset_handle: Option<Vec<u8>>,
36 pub transitions: Option<Vec<ScheduleTransition>>,
37 pub built_in: Option<bool>,
38}
39
40#[derive(Debug, serde::Serialize)]
41pub struct ScheduleTransition {
42 pub day_of_week: Option<u8>,
43 pub transition_time: Option<u16>,
44 pub preset_handle: Option<Vec<u8>>,
45 pub system_mode: Option<u8>,
46 pub cooling_setpoint: Option<u8>,
47 pub heating_setpoint: Option<u8>,
48}
49
50#[derive(Debug, serde::Serialize)]
51pub struct ScheduleType {
52 pub system_mode: Option<u8>,
53 pub number_of_schedules: Option<u8>,
54 pub schedule_type_features: Option<u8>,
55}
56
57#[derive(Debug, serde::Serialize)]
58pub struct WeeklyScheduleTransition {
59 pub transition_time: Option<u16>,
60 pub heat_setpoint: Option<u8>,
61 pub cool_setpoint: Option<u8>,
62}
63
64pub fn encode_setpoint_raise_lower(mode: u8, amount: i8) -> anyhow::Result<Vec<u8>> {
68 let tlv = tlv::TlvItemEnc {
69 tag: 0,
70 value: tlv::TlvItemValueEnc::StructInvisible(vec![
71 (0, tlv::TlvItemValueEnc::UInt8(mode)).into(),
72 (1, tlv::TlvItemValueEnc::Int8(amount)).into(),
73 ]),
74 };
75 Ok(tlv.encode()?)
76}
77
78pub fn encode_set_weekly_schedule(number_of_transitions_for_sequence: u8, day_of_week_for_sequence: u8, mode_for_sequence: u8, transitions: Vec<u8>) -> anyhow::Result<Vec<u8>> {
80 let tlv = tlv::TlvItemEnc {
81 tag: 0,
82 value: tlv::TlvItemValueEnc::StructInvisible(vec![
83 (0, tlv::TlvItemValueEnc::UInt8(number_of_transitions_for_sequence)).into(),
84 (1, tlv::TlvItemValueEnc::UInt8(day_of_week_for_sequence)).into(),
85 (2, tlv::TlvItemValueEnc::UInt8(mode_for_sequence)).into(),
86 (3, tlv::TlvItemValueEnc::StructAnon(transitions.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
87 ]),
88 };
89 Ok(tlv.encode()?)
90}
91
92pub fn encode_get_weekly_schedule(days_to_return: u8, mode_to_return: u8) -> anyhow::Result<Vec<u8>> {
94 let tlv = tlv::TlvItemEnc {
95 tag: 0,
96 value: tlv::TlvItemValueEnc::StructInvisible(vec![
97 (0, tlv::TlvItemValueEnc::UInt8(days_to_return)).into(),
98 (1, tlv::TlvItemValueEnc::UInt8(mode_to_return)).into(),
99 ]),
100 };
101 Ok(tlv.encode()?)
102}
103
104pub fn encode_set_active_schedule_request(schedule_handle: Vec<u8>) -> anyhow::Result<Vec<u8>> {
106 let tlv = tlv::TlvItemEnc {
107 tag: 0,
108 value: tlv::TlvItemValueEnc::StructInvisible(vec![
109 (0, tlv::TlvItemValueEnc::OctetString(schedule_handle)).into(),
110 ]),
111 };
112 Ok(tlv.encode()?)
113}
114
115pub fn encode_set_active_preset_request(preset_handle: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
117 let tlv = tlv::TlvItemEnc {
118 tag: 0,
119 value: tlv::TlvItemValueEnc::StructInvisible(vec![
120 (0, tlv::TlvItemValueEnc::OctetString(preset_handle.unwrap_or(vec![]))).into(),
121 ]),
122 };
123 Ok(tlv.encode()?)
124}
125
126pub fn decode_local_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
130 if let tlv::TlvItemValue::Int(v) = inp {
131 Ok(Some(*v as u8))
132 } else {
133 Ok(None)
134 }
135}
136
137pub fn decode_outdoor_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
139 if let tlv::TlvItemValue::Int(v) = inp {
140 Ok(Some(*v as u8))
141 } else {
142 Ok(None)
143 }
144}
145
146pub fn decode_occupancy(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
148 if let tlv::TlvItemValue::Int(v) = inp {
149 Ok(*v as u8)
150 } else {
151 Err(anyhow::anyhow!("Expected Integer"))
152 }
153}
154
155pub fn decode_abs_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
157 if let tlv::TlvItemValue::Int(v) = inp {
158 Ok(*v as u8)
159 } else {
160 Err(anyhow::anyhow!("Expected Integer"))
161 }
162}
163
164pub fn decode_abs_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
166 if let tlv::TlvItemValue::Int(v) = inp {
167 Ok(*v as u8)
168 } else {
169 Err(anyhow::anyhow!("Expected Integer"))
170 }
171}
172
173pub fn decode_abs_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
175 if let tlv::TlvItemValue::Int(v) = inp {
176 Ok(*v as u8)
177 } else {
178 Err(anyhow::anyhow!("Expected Integer"))
179 }
180}
181
182pub fn decode_abs_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
184 if let tlv::TlvItemValue::Int(v) = inp {
185 Ok(*v as u8)
186 } else {
187 Err(anyhow::anyhow!("Expected Integer"))
188 }
189}
190
191pub fn decode_pi_cooling_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
193 if let tlv::TlvItemValue::Int(v) = inp {
194 Ok(*v as u8)
195 } else {
196 Err(anyhow::anyhow!("Expected Integer"))
197 }
198}
199
200pub fn decode_pi_heating_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
202 if let tlv::TlvItemValue::Int(v) = inp {
203 Ok(*v as u8)
204 } else {
205 Err(anyhow::anyhow!("Expected Integer"))
206 }
207}
208
209pub fn decode_hvac_system_type_configuration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
211 if let tlv::TlvItemValue::Int(v) = inp {
212 Ok(*v as u8)
213 } else {
214 Err(anyhow::anyhow!("Expected Integer"))
215 }
216}
217
218pub fn decode_local_temperature_calibration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
220 if let tlv::TlvItemValue::Int(v) = inp {
221 Ok(*v as u8)
222 } else {
223 Err(anyhow::anyhow!("Expected Integer"))
224 }
225}
226
227pub fn decode_occupied_cooling_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
229 if let tlv::TlvItemValue::Int(v) = inp {
230 Ok(*v as u8)
231 } else {
232 Err(anyhow::anyhow!("Expected Integer"))
233 }
234}
235
236pub fn decode_occupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
238 if let tlv::TlvItemValue::Int(v) = inp {
239 Ok(*v as u8)
240 } else {
241 Err(anyhow::anyhow!("Expected Integer"))
242 }
243}
244
245pub fn decode_unoccupied_cooling_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
247 if let tlv::TlvItemValue::Int(v) = inp {
248 Ok(*v as u8)
249 } else {
250 Err(anyhow::anyhow!("Expected Integer"))
251 }
252}
253
254pub fn decode_unoccupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
256 if let tlv::TlvItemValue::Int(v) = inp {
257 Ok(*v as u8)
258 } else {
259 Err(anyhow::anyhow!("Expected Integer"))
260 }
261}
262
263pub fn decode_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
265 if let tlv::TlvItemValue::Int(v) = inp {
266 Ok(*v as u8)
267 } else {
268 Err(anyhow::anyhow!("Expected Integer"))
269 }
270}
271
272pub fn decode_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
274 if let tlv::TlvItemValue::Int(v) = inp {
275 Ok(*v as u8)
276 } else {
277 Err(anyhow::anyhow!("Expected Integer"))
278 }
279}
280
281pub fn decode_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
283 if let tlv::TlvItemValue::Int(v) = inp {
284 Ok(*v as u8)
285 } else {
286 Err(anyhow::anyhow!("Expected Integer"))
287 }
288}
289
290pub fn decode_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
292 if let tlv::TlvItemValue::Int(v) = inp {
293 Ok(*v as u8)
294 } else {
295 Err(anyhow::anyhow!("Expected Integer"))
296 }
297}
298
299pub fn decode_min_setpoint_dead_band(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
301 if let tlv::TlvItemValue::Int(v) = inp {
302 Ok(*v as u8)
303 } else {
304 Err(anyhow::anyhow!("Expected Integer"))
305 }
306}
307
308pub fn decode_remote_sensing(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
310 if let tlv::TlvItemValue::Int(v) = inp {
311 Ok(*v as u8)
312 } else {
313 Err(anyhow::anyhow!("Expected Integer"))
314 }
315}
316
317pub fn decode_control_sequence_of_operation(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
319 if let tlv::TlvItemValue::Int(v) = inp {
320 Ok(*v as u8)
321 } else {
322 Err(anyhow::anyhow!("Expected Integer"))
323 }
324}
325
326pub fn decode_system_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
328 if let tlv::TlvItemValue::Int(v) = inp {
329 Ok(*v as u8)
330 } else {
331 Err(anyhow::anyhow!("Expected Integer"))
332 }
333}
334
335pub fn decode_thermostat_running_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
337 if let tlv::TlvItemValue::Int(v) = inp {
338 Ok(*v as u8)
339 } else {
340 Err(anyhow::anyhow!("Expected Integer"))
341 }
342}
343
344pub fn decode_start_of_week(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
346 if let tlv::TlvItemValue::Int(v) = inp {
347 Ok(*v as u8)
348 } else {
349 Err(anyhow::anyhow!("Expected Integer"))
350 }
351}
352
353pub fn decode_number_of_weekly_transitions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
355 if let tlv::TlvItemValue::Int(v) = inp {
356 Ok(*v as u8)
357 } else {
358 Err(anyhow::anyhow!("Expected Integer"))
359 }
360}
361
362pub fn decode_number_of_daily_transitions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
364 if let tlv::TlvItemValue::Int(v) = inp {
365 Ok(*v as u8)
366 } else {
367 Err(anyhow::anyhow!("Expected Integer"))
368 }
369}
370
371pub fn decode_temperature_setpoint_hold(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
373 if let tlv::TlvItemValue::Int(v) = inp {
374 Ok(*v as u8)
375 } else {
376 Err(anyhow::anyhow!("Expected Integer"))
377 }
378}
379
380pub fn decode_temperature_setpoint_hold_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
382 if let tlv::TlvItemValue::Int(v) = inp {
383 Ok(Some(*v as u16))
384 } else {
385 Ok(None)
386 }
387}
388
389pub fn decode_thermostat_programming_operation_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
391 if let tlv::TlvItemValue::Int(v) = inp {
392 Ok(*v as u8)
393 } else {
394 Err(anyhow::anyhow!("Expected Integer"))
395 }
396}
397
398pub fn decode_thermostat_running_state(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
400 if let tlv::TlvItemValue::Int(v) = inp {
401 Ok(*v as u8)
402 } else {
403 Err(anyhow::anyhow!("Expected Integer"))
404 }
405}
406
407pub fn decode_setpoint_change_source(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
409 if let tlv::TlvItemValue::Int(v) = inp {
410 Ok(*v as u8)
411 } else {
412 Err(anyhow::anyhow!("Expected Integer"))
413 }
414}
415
416pub fn decode_setpoint_change_amount(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
418 if let tlv::TlvItemValue::Int(v) = inp {
419 Ok(Some(*v as u8))
420 } else {
421 Ok(None)
422 }
423}
424
425pub fn decode_setpoint_change_source_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
427 if let tlv::TlvItemValue::Int(v) = inp {
428 Ok(*v)
429 } else {
430 Err(anyhow::anyhow!("Expected Integer"))
431 }
432}
433
434pub fn decode_occupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
436 if let tlv::TlvItemValue::Int(v) = inp {
437 Ok(Some(*v as u8))
438 } else {
439 Ok(None)
440 }
441}
442
443pub fn decode_occupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
445 if let tlv::TlvItemValue::Int(v) = inp {
446 Ok(Some(*v as u8))
447 } else {
448 Ok(None)
449 }
450}
451
452pub fn decode_occupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
454 if let tlv::TlvItemValue::Int(v) = inp {
455 Ok(Some(*v as u8))
456 } else {
457 Ok(None)
458 }
459}
460
461pub fn decode_unoccupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
463 if let tlv::TlvItemValue::Int(v) = inp {
464 Ok(Some(*v as u8))
465 } else {
466 Ok(None)
467 }
468}
469
470pub fn decode_unoccupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
472 if let tlv::TlvItemValue::Int(v) = inp {
473 Ok(Some(*v as u8))
474 } else {
475 Ok(None)
476 }
477}
478
479pub fn decode_unoccupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
481 if let tlv::TlvItemValue::Int(v) = inp {
482 Ok(Some(*v as u8))
483 } else {
484 Ok(None)
485 }
486}
487
488pub fn decode_emergency_heat_delta(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
490 if let tlv::TlvItemValue::Int(v) = inp {
491 Ok(*v as u8)
492 } else {
493 Err(anyhow::anyhow!("Expected Integer"))
494 }
495}
496
497pub fn decode_ac_type(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
499 if let tlv::TlvItemValue::Int(v) = inp {
500 Ok(*v as u8)
501 } else {
502 Err(anyhow::anyhow!("Expected Integer"))
503 }
504}
505
506pub fn decode_ac_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
508 if let tlv::TlvItemValue::Int(v) = inp {
509 Ok(*v as u16)
510 } else {
511 Err(anyhow::anyhow!("Expected Integer"))
512 }
513}
514
515pub fn decode_ac_refrigerant_type(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
517 if let tlv::TlvItemValue::Int(v) = inp {
518 Ok(*v as u8)
519 } else {
520 Err(anyhow::anyhow!("Expected Integer"))
521 }
522}
523
524pub fn decode_ac_compressor_type(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
526 if let tlv::TlvItemValue::Int(v) = inp {
527 Ok(*v as u8)
528 } else {
529 Err(anyhow::anyhow!("Expected Integer"))
530 }
531}
532
533pub fn decode_ac_error_code(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
535 if let tlv::TlvItemValue::Int(v) = inp {
536 Ok(*v as u8)
537 } else {
538 Err(anyhow::anyhow!("Expected Integer"))
539 }
540}
541
542pub fn decode_aclouver_position(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
544 if let tlv::TlvItemValue::Int(v) = inp {
545 Ok(*v as u8)
546 } else {
547 Err(anyhow::anyhow!("Expected Integer"))
548 }
549}
550
551pub fn decode_ac_coil_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
553 if let tlv::TlvItemValue::Int(v) = inp {
554 Ok(Some(*v as u8))
555 } else {
556 Ok(None)
557 }
558}
559
560pub fn decode_ac_capacity_format(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
562 if let tlv::TlvItemValue::Int(v) = inp {
563 Ok(*v as u8)
564 } else {
565 Err(anyhow::anyhow!("Expected Integer"))
566 }
567}
568
569pub fn decode_preset_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<PresetType>> {
571 let mut res = Vec::new();
572 if let tlv::TlvItemValue::List(v) = inp {
573 for item in v {
574 res.push(PresetType {
575 preset_scenario: item.get_int(&[0]).map(|v| v as u8),
576 number_of_presets: item.get_int(&[1]).map(|v| v as u8),
577 preset_type_features: item.get_int(&[2]).map(|v| v as u8),
578 });
579 }
580 }
581 Ok(res)
582}
583
584pub fn decode_schedule_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ScheduleType>> {
586 let mut res = Vec::new();
587 if let tlv::TlvItemValue::List(v) = inp {
588 for item in v {
589 res.push(ScheduleType {
590 system_mode: item.get_int(&[0]).map(|v| v as u8),
591 number_of_schedules: item.get_int(&[1]).map(|v| v as u8),
592 schedule_type_features: item.get_int(&[2]).map(|v| v as u8),
593 });
594 }
595 }
596 Ok(res)
597}
598
599pub fn decode_number_of_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
601 if let tlv::TlvItemValue::Int(v) = inp {
602 Ok(*v as u8)
603 } else {
604 Err(anyhow::anyhow!("Expected Integer"))
605 }
606}
607
608pub fn decode_number_of_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
610 if let tlv::TlvItemValue::Int(v) = inp {
611 Ok(*v as u8)
612 } else {
613 Err(anyhow::anyhow!("Expected Integer"))
614 }
615}
616
617pub fn decode_number_of_schedule_transitions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
619 if let tlv::TlvItemValue::Int(v) = inp {
620 Ok(*v as u8)
621 } else {
622 Err(anyhow::anyhow!("Expected Integer"))
623 }
624}
625
626pub fn decode_number_of_schedule_transition_per_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
628 if let tlv::TlvItemValue::Int(v) = inp {
629 Ok(Some(*v as u8))
630 } else {
631 Ok(None)
632 }
633}
634
635pub fn decode_active_preset_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
637 if let tlv::TlvItemValue::OctetString(v) = inp {
638 Ok(Some(v.clone()))
639 } else {
640 Ok(None)
641 }
642}
643
644pub fn decode_active_schedule_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
646 if let tlv::TlvItemValue::OctetString(v) = inp {
647 Ok(Some(v.clone()))
648 } else {
649 Ok(None)
650 }
651}
652
653pub fn decode_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Preset>> {
655 let mut res = Vec::new();
656 if let tlv::TlvItemValue::List(v) = inp {
657 for item in v {
658 res.push(Preset {
659 preset_handle: item.get_octet_string_owned(&[0]),
660 preset_scenario: item.get_int(&[1]).map(|v| v as u8),
661 name: item.get_string_owned(&[2]),
662 cooling_setpoint: item.get_int(&[3]).map(|v| v as u8),
663 heating_setpoint: item.get_int(&[4]).map(|v| v as u8),
664 built_in: item.get_bool(&[5]),
665 });
666 }
667 }
668 Ok(res)
669}
670
671pub fn decode_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Schedule>> {
673 let mut res = Vec::new();
674 if let tlv::TlvItemValue::List(v) = inp {
675 for item in v {
676 res.push(Schedule {
677 schedule_handle: item.get_octet_string_owned(&[0]),
678 system_mode: item.get_int(&[1]).map(|v| v as u8),
679 name: item.get_string_owned(&[2]),
680 preset_handle: item.get_octet_string_owned(&[3]),
681 transitions: {
682 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
683 let mut items = Vec::new();
684 for list_item in l {
685 items.push(ScheduleTransition {
686 day_of_week: list_item.get_int(&[0]).map(|v| v as u8),
687 transition_time: list_item.get_int(&[1]).map(|v| v as u16),
688 preset_handle: list_item.get_octet_string_owned(&[2]),
689 system_mode: list_item.get_int(&[3]).map(|v| v as u8),
690 cooling_setpoint: list_item.get_int(&[4]).map(|v| v as u8),
691 heating_setpoint: list_item.get_int(&[5]).map(|v| v as u8),
692 });
693 }
694 Some(items)
695 } else {
696 None
697 }
698 },
699 built_in: item.get_bool(&[5]),
700 });
701 }
702 }
703 Ok(res)
704}
705
706pub fn decode_setpoint_hold_expiry_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
708 if let tlv::TlvItemValue::Int(v) = inp {
709 Ok(Some(*v))
710 } else {
711 Ok(None)
712 }
713}
714
715
716pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
728 if cluster_id != 0x0201 {
730 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0201, got {}\"}}", cluster_id);
731 }
732
733 match attribute_id {
734 0x0000 => {
735 match decode_local_temperature(tlv_value) {
736 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
737 Err(e) => format!("{{\"error\": \"{}\"}}", e),
738 }
739 }
740 0x0001 => {
741 match decode_outdoor_temperature(tlv_value) {
742 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
743 Err(e) => format!("{{\"error\": \"{}\"}}", e),
744 }
745 }
746 0x0002 => {
747 match decode_occupancy(tlv_value) {
748 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
749 Err(e) => format!("{{\"error\": \"{}\"}}", e),
750 }
751 }
752 0x0003 => {
753 match decode_abs_min_heat_setpoint_limit(tlv_value) {
754 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
755 Err(e) => format!("{{\"error\": \"{}\"}}", e),
756 }
757 }
758 0x0004 => {
759 match decode_abs_max_heat_setpoint_limit(tlv_value) {
760 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
761 Err(e) => format!("{{\"error\": \"{}\"}}", e),
762 }
763 }
764 0x0005 => {
765 match decode_abs_min_cool_setpoint_limit(tlv_value) {
766 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
767 Err(e) => format!("{{\"error\": \"{}\"}}", e),
768 }
769 }
770 0x0006 => {
771 match decode_abs_max_cool_setpoint_limit(tlv_value) {
772 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
773 Err(e) => format!("{{\"error\": \"{}\"}}", e),
774 }
775 }
776 0x0007 => {
777 match decode_pi_cooling_demand(tlv_value) {
778 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
779 Err(e) => format!("{{\"error\": \"{}\"}}", e),
780 }
781 }
782 0x0008 => {
783 match decode_pi_heating_demand(tlv_value) {
784 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
785 Err(e) => format!("{{\"error\": \"{}\"}}", e),
786 }
787 }
788 0x0009 => {
789 match decode_hvac_system_type_configuration(tlv_value) {
790 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
791 Err(e) => format!("{{\"error\": \"{}\"}}", e),
792 }
793 }
794 0x0010 => {
795 match decode_local_temperature_calibration(tlv_value) {
796 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
797 Err(e) => format!("{{\"error\": \"{}\"}}", e),
798 }
799 }
800 0x0011 => {
801 match decode_occupied_cooling_setpoint(tlv_value) {
802 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
803 Err(e) => format!("{{\"error\": \"{}\"}}", e),
804 }
805 }
806 0x0012 => {
807 match decode_occupied_heating_setpoint(tlv_value) {
808 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
809 Err(e) => format!("{{\"error\": \"{}\"}}", e),
810 }
811 }
812 0x0013 => {
813 match decode_unoccupied_cooling_setpoint(tlv_value) {
814 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
815 Err(e) => format!("{{\"error\": \"{}\"}}", e),
816 }
817 }
818 0x0014 => {
819 match decode_unoccupied_heating_setpoint(tlv_value) {
820 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
821 Err(e) => format!("{{\"error\": \"{}\"}}", e),
822 }
823 }
824 0x0015 => {
825 match decode_min_heat_setpoint_limit(tlv_value) {
826 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
827 Err(e) => format!("{{\"error\": \"{}\"}}", e),
828 }
829 }
830 0x0016 => {
831 match decode_max_heat_setpoint_limit(tlv_value) {
832 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
833 Err(e) => format!("{{\"error\": \"{}\"}}", e),
834 }
835 }
836 0x0017 => {
837 match decode_min_cool_setpoint_limit(tlv_value) {
838 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
839 Err(e) => format!("{{\"error\": \"{}\"}}", e),
840 }
841 }
842 0x0018 => {
843 match decode_max_cool_setpoint_limit(tlv_value) {
844 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
845 Err(e) => format!("{{\"error\": \"{}\"}}", e),
846 }
847 }
848 0x0019 => {
849 match decode_min_setpoint_dead_band(tlv_value) {
850 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
851 Err(e) => format!("{{\"error\": \"{}\"}}", e),
852 }
853 }
854 0x001A => {
855 match decode_remote_sensing(tlv_value) {
856 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
857 Err(e) => format!("{{\"error\": \"{}\"}}", e),
858 }
859 }
860 0x001B => {
861 match decode_control_sequence_of_operation(tlv_value) {
862 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
863 Err(e) => format!("{{\"error\": \"{}\"}}", e),
864 }
865 }
866 0x001C => {
867 match decode_system_mode(tlv_value) {
868 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
869 Err(e) => format!("{{\"error\": \"{}\"}}", e),
870 }
871 }
872 0x001E => {
873 match decode_thermostat_running_mode(tlv_value) {
874 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
875 Err(e) => format!("{{\"error\": \"{}\"}}", e),
876 }
877 }
878 0x0020 => {
879 match decode_start_of_week(tlv_value) {
880 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
881 Err(e) => format!("{{\"error\": \"{}\"}}", e),
882 }
883 }
884 0x0021 => {
885 match decode_number_of_weekly_transitions(tlv_value) {
886 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
887 Err(e) => format!("{{\"error\": \"{}\"}}", e),
888 }
889 }
890 0x0022 => {
891 match decode_number_of_daily_transitions(tlv_value) {
892 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
893 Err(e) => format!("{{\"error\": \"{}\"}}", e),
894 }
895 }
896 0x0023 => {
897 match decode_temperature_setpoint_hold(tlv_value) {
898 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
899 Err(e) => format!("{{\"error\": \"{}\"}}", e),
900 }
901 }
902 0x0024 => {
903 match decode_temperature_setpoint_hold_duration(tlv_value) {
904 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
905 Err(e) => format!("{{\"error\": \"{}\"}}", e),
906 }
907 }
908 0x0025 => {
909 match decode_thermostat_programming_operation_mode(tlv_value) {
910 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
911 Err(e) => format!("{{\"error\": \"{}\"}}", e),
912 }
913 }
914 0x0029 => {
915 match decode_thermostat_running_state(tlv_value) {
916 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
917 Err(e) => format!("{{\"error\": \"{}\"}}", e),
918 }
919 }
920 0x0030 => {
921 match decode_setpoint_change_source(tlv_value) {
922 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
923 Err(e) => format!("{{\"error\": \"{}\"}}", e),
924 }
925 }
926 0x0031 => {
927 match decode_setpoint_change_amount(tlv_value) {
928 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
929 Err(e) => format!("{{\"error\": \"{}\"}}", e),
930 }
931 }
932 0x0032 => {
933 match decode_setpoint_change_source_timestamp(tlv_value) {
934 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
935 Err(e) => format!("{{\"error\": \"{}\"}}", e),
936 }
937 }
938 0x0034 => {
939 match decode_occupied_setback(tlv_value) {
940 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
941 Err(e) => format!("{{\"error\": \"{}\"}}", e),
942 }
943 }
944 0x0035 => {
945 match decode_occupied_setback_min(tlv_value) {
946 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
947 Err(e) => format!("{{\"error\": \"{}\"}}", e),
948 }
949 }
950 0x0036 => {
951 match decode_occupied_setback_max(tlv_value) {
952 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
953 Err(e) => format!("{{\"error\": \"{}\"}}", e),
954 }
955 }
956 0x0037 => {
957 match decode_unoccupied_setback(tlv_value) {
958 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
959 Err(e) => format!("{{\"error\": \"{}\"}}", e),
960 }
961 }
962 0x0038 => {
963 match decode_unoccupied_setback_min(tlv_value) {
964 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
965 Err(e) => format!("{{\"error\": \"{}\"}}", e),
966 }
967 }
968 0x0039 => {
969 match decode_unoccupied_setback_max(tlv_value) {
970 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
971 Err(e) => format!("{{\"error\": \"{}\"}}", e),
972 }
973 }
974 0x003A => {
975 match decode_emergency_heat_delta(tlv_value) {
976 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
977 Err(e) => format!("{{\"error\": \"{}\"}}", e),
978 }
979 }
980 0x0040 => {
981 match decode_ac_type(tlv_value) {
982 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
983 Err(e) => format!("{{\"error\": \"{}\"}}", e),
984 }
985 }
986 0x0041 => {
987 match decode_ac_capacity(tlv_value) {
988 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
989 Err(e) => format!("{{\"error\": \"{}\"}}", e),
990 }
991 }
992 0x0042 => {
993 match decode_ac_refrigerant_type(tlv_value) {
994 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
995 Err(e) => format!("{{\"error\": \"{}\"}}", e),
996 }
997 }
998 0x0043 => {
999 match decode_ac_compressor_type(tlv_value) {
1000 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1001 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1002 }
1003 }
1004 0x0044 => {
1005 match decode_ac_error_code(tlv_value) {
1006 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1007 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1008 }
1009 }
1010 0x0045 => {
1011 match decode_aclouver_position(tlv_value) {
1012 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1013 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1014 }
1015 }
1016 0x0046 => {
1017 match decode_ac_coil_temperature(tlv_value) {
1018 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1019 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1020 }
1021 }
1022 0x0047 => {
1023 match decode_ac_capacity_format(tlv_value) {
1024 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1025 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1026 }
1027 }
1028 0x0048 => {
1029 match decode_preset_types(tlv_value) {
1030 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1031 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1032 }
1033 }
1034 0x0049 => {
1035 match decode_schedule_types(tlv_value) {
1036 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1037 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1038 }
1039 }
1040 0x004A => {
1041 match decode_number_of_presets(tlv_value) {
1042 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1043 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1044 }
1045 }
1046 0x004B => {
1047 match decode_number_of_schedules(tlv_value) {
1048 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1049 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1050 }
1051 }
1052 0x004C => {
1053 match decode_number_of_schedule_transitions(tlv_value) {
1054 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1055 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1056 }
1057 }
1058 0x004D => {
1059 match decode_number_of_schedule_transition_per_day(tlv_value) {
1060 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1061 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1062 }
1063 }
1064 0x004E => {
1065 match decode_active_preset_handle(tlv_value) {
1066 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1067 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1068 }
1069 }
1070 0x004F => {
1071 match decode_active_schedule_handle(tlv_value) {
1072 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1073 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1074 }
1075 }
1076 0x0050 => {
1077 match decode_presets(tlv_value) {
1078 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1079 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1080 }
1081 }
1082 0x0051 => {
1083 match decode_schedules(tlv_value) {
1084 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1085 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1086 }
1087 }
1088 0x0052 => {
1089 match decode_setpoint_hold_expiry_timestamp(tlv_value) {
1090 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1091 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1092 }
1093 }
1094 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
1095 }
1096}
1097
1098pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
1103 vec![
1104 (0x0000, "LocalTemperature"),
1105 (0x0001, "OutdoorTemperature"),
1106 (0x0002, "Occupancy"),
1107 (0x0003, "AbsMinHeatSetpointLimit"),
1108 (0x0004, "AbsMaxHeatSetpointLimit"),
1109 (0x0005, "AbsMinCoolSetpointLimit"),
1110 (0x0006, "AbsMaxCoolSetpointLimit"),
1111 (0x0007, "PICoolingDemand"),
1112 (0x0008, "PIHeatingDemand"),
1113 (0x0009, "HVACSystemTypeConfiguration"),
1114 (0x0010, "LocalTemperatureCalibration"),
1115 (0x0011, "OccupiedCoolingSetpoint"),
1116 (0x0012, "OccupiedHeatingSetpoint"),
1117 (0x0013, "UnoccupiedCoolingSetpoint"),
1118 (0x0014, "UnoccupiedHeatingSetpoint"),
1119 (0x0015, "MinHeatSetpointLimit"),
1120 (0x0016, "MaxHeatSetpointLimit"),
1121 (0x0017, "MinCoolSetpointLimit"),
1122 (0x0018, "MaxCoolSetpointLimit"),
1123 (0x0019, "MinSetpointDeadBand"),
1124 (0x001A, "RemoteSensing"),
1125 (0x001B, "ControlSequenceOfOperation"),
1126 (0x001C, "SystemMode"),
1127 (0x001E, "ThermostatRunningMode"),
1128 (0x0020, "StartOfWeek"),
1129 (0x0021, "NumberOfWeeklyTransitions"),
1130 (0x0022, "NumberOfDailyTransitions"),
1131 (0x0023, "TemperatureSetpointHold"),
1132 (0x0024, "TemperatureSetpointHoldDuration"),
1133 (0x0025, "ThermostatProgrammingOperationMode"),
1134 (0x0029, "ThermostatRunningState"),
1135 (0x0030, "SetpointChangeSource"),
1136 (0x0031, "SetpointChangeAmount"),
1137 (0x0032, "SetpointChangeSourceTimestamp"),
1138 (0x0034, "OccupiedSetback"),
1139 (0x0035, "OccupiedSetbackMin"),
1140 (0x0036, "OccupiedSetbackMax"),
1141 (0x0037, "UnoccupiedSetback"),
1142 (0x0038, "UnoccupiedSetbackMin"),
1143 (0x0039, "UnoccupiedSetbackMax"),
1144 (0x003A, "EmergencyHeatDelta"),
1145 (0x0040, "ACType"),
1146 (0x0041, "ACCapacity"),
1147 (0x0042, "ACRefrigerantType"),
1148 (0x0043, "ACCompressorType"),
1149 (0x0044, "ACErrorCode"),
1150 (0x0045, "ACLouverPosition"),
1151 (0x0046, "ACCoilTemperature"),
1152 (0x0047, "ACCapacityFormat"),
1153 (0x0048, "PresetTypes"),
1154 (0x0049, "ScheduleTypes"),
1155 (0x004A, "NumberOfPresets"),
1156 (0x004B, "NumberOfSchedules"),
1157 (0x004C, "NumberOfScheduleTransitions"),
1158 (0x004D, "NumberOfScheduleTransitionPerDay"),
1159 (0x004E, "ActivePresetHandle"),
1160 (0x004F, "ActiveScheduleHandle"),
1161 (0x0050, "Presets"),
1162 (0x0051, "Schedules"),
1163 (0x0052, "SetpointHoldExpiryTimestamp"),
1164 ]
1165}
1166