1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum ACCapacityFormat {
19 Btuh = 0,
21}
22
23impl ACCapacityFormat {
24 pub fn from_u8(value: u8) -> Option<Self> {
26 match value {
27 0 => Some(ACCapacityFormat::Btuh),
28 _ => None,
29 }
30 }
31
32 pub fn to_u8(self) -> u8 {
34 self as u8
35 }
36}
37
38impl From<ACCapacityFormat> for u8 {
39 fn from(val: ACCapacityFormat) -> Self {
40 val as u8
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[repr(u8)]
46pub enum ACCompressorType {
47 Unknown = 0,
49 T1 = 1,
51 T2 = 2,
53 T3 = 3,
55}
56
57impl ACCompressorType {
58 pub fn from_u8(value: u8) -> Option<Self> {
60 match value {
61 0 => Some(ACCompressorType::Unknown),
62 1 => Some(ACCompressorType::T1),
63 2 => Some(ACCompressorType::T2),
64 3 => Some(ACCompressorType::T3),
65 _ => None,
66 }
67 }
68
69 pub fn to_u8(self) -> u8 {
71 self as u8
72 }
73}
74
75impl From<ACCompressorType> for u8 {
76 fn from(val: ACCompressorType) -> Self {
77 val as u8
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
82#[repr(u8)]
83pub enum ACLouverPosition {
84 Closed = 1,
86 Open = 2,
88 Quarter = 3,
90 Half = 4,
92 Threequarters = 5,
94}
95
96impl ACLouverPosition {
97 pub fn from_u8(value: u8) -> Option<Self> {
99 match value {
100 1 => Some(ACLouverPosition::Closed),
101 2 => Some(ACLouverPosition::Open),
102 3 => Some(ACLouverPosition::Quarter),
103 4 => Some(ACLouverPosition::Half),
104 5 => Some(ACLouverPosition::Threequarters),
105 _ => None,
106 }
107 }
108
109 pub fn to_u8(self) -> u8 {
111 self as u8
112 }
113}
114
115impl From<ACLouverPosition> for u8 {
116 fn from(val: ACLouverPosition) -> Self {
117 val as u8
118 }
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
122#[repr(u8)]
123pub enum ACRefrigerantType {
124 Unknown = 0,
126 R22 = 1,
128 R410a = 2,
130 R407c = 3,
132}
133
134impl ACRefrigerantType {
135 pub fn from_u8(value: u8) -> Option<Self> {
137 match value {
138 0 => Some(ACRefrigerantType::Unknown),
139 1 => Some(ACRefrigerantType::R22),
140 2 => Some(ACRefrigerantType::R410a),
141 3 => Some(ACRefrigerantType::R407c),
142 _ => None,
143 }
144 }
145
146 pub fn to_u8(self) -> u8 {
148 self as u8
149 }
150}
151
152impl From<ACRefrigerantType> for u8 {
153 fn from(val: ACRefrigerantType) -> Self {
154 val as u8
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
159#[repr(u8)]
160pub enum ACType {
161 Unknown = 0,
163 Coolingfixed = 1,
165 Heatpumpfixed = 2,
167 Coolinginverter = 3,
169 Heatpumpinverter = 4,
171}
172
173impl ACType {
174 pub fn from_u8(value: u8) -> Option<Self> {
176 match value {
177 0 => Some(ACType::Unknown),
178 1 => Some(ACType::Coolingfixed),
179 2 => Some(ACType::Heatpumpfixed),
180 3 => Some(ACType::Coolinginverter),
181 4 => Some(ACType::Heatpumpinverter),
182 _ => None,
183 }
184 }
185
186 pub fn to_u8(self) -> u8 {
188 self as u8
189 }
190}
191
192impl From<ACType> for u8 {
193 fn from(val: ACType) -> Self {
194 val as u8
195 }
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
199#[repr(u8)]
200pub enum ControlSequenceOfOperation {
201 Coolingonly = 0,
203 Coolingwithreheat = 1,
205 Heatingonly = 2,
207 Heatingwithreheat = 3,
209 Coolingandheating = 4,
211 Coolingandheatingwithreheat = 5,
213}
214
215impl ControlSequenceOfOperation {
216 pub fn from_u8(value: u8) -> Option<Self> {
218 match value {
219 0 => Some(ControlSequenceOfOperation::Coolingonly),
220 1 => Some(ControlSequenceOfOperation::Coolingwithreheat),
221 2 => Some(ControlSequenceOfOperation::Heatingonly),
222 3 => Some(ControlSequenceOfOperation::Heatingwithreheat),
223 4 => Some(ControlSequenceOfOperation::Coolingandheating),
224 5 => Some(ControlSequenceOfOperation::Coolingandheatingwithreheat),
225 _ => None,
226 }
227 }
228
229 pub fn to_u8(self) -> u8 {
231 self as u8
232 }
233}
234
235impl From<ControlSequenceOfOperation> for u8 {
236 fn from(val: ControlSequenceOfOperation) -> Self {
237 val as u8
238 }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
242#[repr(u8)]
243pub enum PresetScenario {
244 Occupied = 1,
246 Unoccupied = 2,
248 Sleep = 3,
250 Wake = 4,
252 Vacation = 5,
254 Goingtosleep = 6,
256 Userdefined = 254,
258}
259
260impl PresetScenario {
261 pub fn from_u8(value: u8) -> Option<Self> {
263 match value {
264 1 => Some(PresetScenario::Occupied),
265 2 => Some(PresetScenario::Unoccupied),
266 3 => Some(PresetScenario::Sleep),
267 4 => Some(PresetScenario::Wake),
268 5 => Some(PresetScenario::Vacation),
269 6 => Some(PresetScenario::Goingtosleep),
270 254 => Some(PresetScenario::Userdefined),
271 _ => None,
272 }
273 }
274
275 pub fn to_u8(self) -> u8 {
277 self as u8
278 }
279}
280
281impl From<PresetScenario> for u8 {
282 fn from(val: PresetScenario) -> Self {
283 val as u8
284 }
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
288#[repr(u8)]
289pub enum SetpointChangeSource {
290 Manual = 0,
292 Schedule = 1,
294 External = 2,
296}
297
298impl SetpointChangeSource {
299 pub fn from_u8(value: u8) -> Option<Self> {
301 match value {
302 0 => Some(SetpointChangeSource::Manual),
303 1 => Some(SetpointChangeSource::Schedule),
304 2 => Some(SetpointChangeSource::External),
305 _ => None,
306 }
307 }
308
309 pub fn to_u8(self) -> u8 {
311 self as u8
312 }
313}
314
315impl From<SetpointChangeSource> for u8 {
316 fn from(val: SetpointChangeSource) -> Self {
317 val as u8
318 }
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
322#[repr(u8)]
323pub enum SetpointRaiseLowerMode {
324 Heat = 0,
326 Cool = 1,
328 Both = 2,
330}
331
332impl SetpointRaiseLowerMode {
333 pub fn from_u8(value: u8) -> Option<Self> {
335 match value {
336 0 => Some(SetpointRaiseLowerMode::Heat),
337 1 => Some(SetpointRaiseLowerMode::Cool),
338 2 => Some(SetpointRaiseLowerMode::Both),
339 _ => None,
340 }
341 }
342
343 pub fn to_u8(self) -> u8 {
345 self as u8
346 }
347}
348
349impl From<SetpointRaiseLowerMode> for u8 {
350 fn from(val: SetpointRaiseLowerMode) -> Self {
351 val as u8
352 }
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
356#[repr(u8)]
357pub enum StartOfWeek {
358 Sunday = 0,
359 Monday = 1,
360 Tuesday = 2,
361 Wednesday = 3,
362 Thursday = 4,
363 Friday = 5,
364 Saturday = 6,
365}
366
367impl StartOfWeek {
368 pub fn from_u8(value: u8) -> Option<Self> {
370 match value {
371 0 => Some(StartOfWeek::Sunday),
372 1 => Some(StartOfWeek::Monday),
373 2 => Some(StartOfWeek::Tuesday),
374 3 => Some(StartOfWeek::Wednesday),
375 4 => Some(StartOfWeek::Thursday),
376 5 => Some(StartOfWeek::Friday),
377 6 => Some(StartOfWeek::Saturday),
378 _ => None,
379 }
380 }
381
382 pub fn to_u8(self) -> u8 {
384 self as u8
385 }
386}
387
388impl From<StartOfWeek> for u8 {
389 fn from(val: StartOfWeek) -> Self {
390 val as u8
391 }
392}
393
394#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
395#[repr(u8)]
396pub enum SystemMode {
397 Off = 0,
399 Auto = 1,
401 Cool = 3,
403 Heat = 4,
405 Emergencyheat = 5,
407 Precooling = 6,
409 Fanonly = 7,
410 Dry = 8,
411 Sleep = 9,
412}
413
414impl SystemMode {
415 pub fn from_u8(value: u8) -> Option<Self> {
417 match value {
418 0 => Some(SystemMode::Off),
419 1 => Some(SystemMode::Auto),
420 3 => Some(SystemMode::Cool),
421 4 => Some(SystemMode::Heat),
422 5 => Some(SystemMode::Emergencyheat),
423 6 => Some(SystemMode::Precooling),
424 7 => Some(SystemMode::Fanonly),
425 8 => Some(SystemMode::Dry),
426 9 => Some(SystemMode::Sleep),
427 _ => None,
428 }
429 }
430
431 pub fn to_u8(self) -> u8 {
433 self as u8
434 }
435}
436
437impl From<SystemMode> for u8 {
438 fn from(val: SystemMode) -> Self {
439 val as u8
440 }
441}
442
443#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
444#[repr(u8)]
445pub enum TemperatureSetpointHold {
446 Setpointholdoff = 0,
448 Setpointholdon = 1,
450}
451
452impl TemperatureSetpointHold {
453 pub fn from_u8(value: u8) -> Option<Self> {
455 match value {
456 0 => Some(TemperatureSetpointHold::Setpointholdoff),
457 1 => Some(TemperatureSetpointHold::Setpointholdon),
458 _ => None,
459 }
460 }
461
462 pub fn to_u8(self) -> u8 {
464 self as u8
465 }
466}
467
468impl From<TemperatureSetpointHold> for u8 {
469 fn from(val: TemperatureSetpointHold) -> Self {
470 val as u8
471 }
472}
473
474#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
475#[repr(u8)]
476pub enum ThermostatRunningMode {
477 Off = 0,
479 Cool = 3,
481 Heat = 4,
483}
484
485impl ThermostatRunningMode {
486 pub fn from_u8(value: u8) -> Option<Self> {
488 match value {
489 0 => Some(ThermostatRunningMode::Off),
490 3 => Some(ThermostatRunningMode::Cool),
491 4 => Some(ThermostatRunningMode::Heat),
492 _ => None,
493 }
494 }
495
496 pub fn to_u8(self) -> u8 {
498 self as u8
499 }
500}
501
502impl From<ThermostatRunningMode> for u8 {
503 fn from(val: ThermostatRunningMode) -> Self {
504 val as u8
505 }
506}
507
508pub type ACErrorCode = u8;
512
513pub mod acerrorcode {
515 pub const COMPRESSOR_FAIL: u8 = 0x01;
517 pub const ROOM_SENSOR_FAIL: u8 = 0x02;
519 pub const OUTDOOR_SENSOR_FAIL: u8 = 0x04;
521 pub const COIL_SENSOR_FAIL: u8 = 0x08;
523 pub const FAN_FAIL: u8 = 0x10;
525}
526
527pub type Occupancy = u8;
529
530pub mod occupancy {
532 pub const OCCUPIED: u8 = 0x01;
534}
535
536pub type PresetTypeFeatures = u8;
538
539pub mod presettypefeatures {
541 pub const AUTOMATIC: u8 = 0x01;
543 pub const SUPPORTS_NAMES: u8 = 0x02;
545}
546
547pub type ProgrammingOperationMode = u8;
549
550pub mod programmingoperationmode {
552 pub const SCHEDULE_ACTIVE: u8 = 0x01;
554 pub const AUTO_RECOVERY: u8 = 0x02;
556 pub const ECONOMY: u8 = 0x04;
558}
559
560pub type RelayState = u8;
562
563pub mod relaystate {
565 pub const HEAT: u8 = 0x01;
567 pub const COOL: u8 = 0x02;
569 pub const FAN: u8 = 0x04;
571 pub const HEAT_STAGE2: u8 = 0x08;
573 pub const COOL_STAGE2: u8 = 0x10;
575 pub const FAN_STAGE2: u8 = 0x20;
577 pub const FAN_STAGE3: u8 = 0x40;
579}
580
581pub type RemoteSensing = u8;
583
584pub mod remotesensing {
586 pub const LOCAL_TEMPERATURE: u8 = 0x01;
588 pub const OUTDOOR_TEMPERATURE: u8 = 0x02;
590 pub const OCCUPANCY: u8 = 0x04;
592}
593
594pub type ScheduleDayOfWeek = u8;
596
597pub mod scheduledayofweek {
599 pub const SUNDAY: u8 = 0x01;
601 pub const MONDAY: u8 = 0x02;
603 pub const TUESDAY: u8 = 0x04;
605 pub const WEDNESDAY: u8 = 0x08;
607 pub const THURSDAY: u8 = 0x10;
609 pub const FRIDAY: u8 = 0x20;
611 pub const SATURDAY: u8 = 0x40;
613 pub const AWAY: u8 = 0x80;
615}
616
617pub type ScheduleMode = u8;
619
620pub mod schedulemode {
622 pub const HEAT_SETPOINT_PRESENT: u8 = 0x01;
624 pub const COOL_SETPOINT_PRESENT: u8 = 0x02;
626}
627
628pub type ScheduleTypeFeatures = u8;
630
631pub mod scheduletypefeatures {
633 pub const SUPPORTS_PRESETS: u8 = 0x01;
635 pub const SUPPORTS_SETPOINTS: u8 = 0x02;
637 pub const SUPPORTS_NAMES: u8 = 0x04;
639 pub const SUPPORTS_OFF: u8 = 0x08;
641}
642
643#[derive(Debug, serde::Serialize)]
646pub struct Preset {
647 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
648 pub preset_handle: Option<Vec<u8>>,
649 pub preset_scenario: Option<PresetScenario>,
650 pub name: Option<String>,
651 pub cooling_setpoint: Option<i16>,
652 pub heating_setpoint: Option<i16>,
653 pub built_in: Option<bool>,
654}
655
656#[derive(Debug, serde::Serialize)]
657pub struct PresetType {
658 pub preset_scenario: Option<PresetScenario>,
659 pub number_of_presets: Option<u8>,
660 pub preset_type_features: Option<PresetTypeFeatures>,
661}
662
663#[derive(Debug, serde::Serialize)]
664pub struct Schedule {
665 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
666 pub schedule_handle: Option<Vec<u8>>,
667 pub system_mode: Option<SystemMode>,
668 pub name: Option<String>,
669 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
670 pub preset_handle: Option<Vec<u8>>,
671 pub transitions: Option<Vec<ScheduleTransition>>,
672 pub built_in: Option<bool>,
673}
674
675#[derive(Debug, serde::Serialize)]
676pub struct ScheduleTransition {
677 pub day_of_week: Option<ScheduleDayOfWeek>,
678 pub transition_time: Option<u16>,
679 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
680 pub preset_handle: Option<Vec<u8>>,
681 pub system_mode: Option<SystemMode>,
682 pub cooling_setpoint: Option<i16>,
683 pub heating_setpoint: Option<i16>,
684}
685
686#[derive(Debug, serde::Serialize)]
687pub struct ScheduleType {
688 pub system_mode: Option<SystemMode>,
689 pub number_of_schedules: Option<u8>,
690 pub schedule_type_features: Option<ScheduleTypeFeatures>,
691}
692
693#[derive(Debug, serde::Serialize)]
694pub struct WeeklyScheduleTransition {
695 pub transition_time: Option<u16>,
696 pub heat_setpoint: Option<i16>,
697 pub cool_setpoint: Option<i16>,
698}
699
700pub fn encode_setpoint_raise_lower(mode: SetpointRaiseLowerMode, amount: i8) -> anyhow::Result<Vec<u8>> {
704 let tlv = tlv::TlvItemEnc {
705 tag: 0,
706 value: tlv::TlvItemValueEnc::StructInvisible(vec![
707 (0, tlv::TlvItemValueEnc::UInt8(mode.to_u8())).into(),
708 (1, tlv::TlvItemValueEnc::Int8(amount)).into(),
709 ]),
710 };
711 Ok(tlv.encode()?)
712}
713
714pub fn encode_set_active_schedule_request(schedule_handle: Vec<u8>) -> anyhow::Result<Vec<u8>> {
716 let tlv = tlv::TlvItemEnc {
717 tag: 0,
718 value: tlv::TlvItemValueEnc::StructInvisible(vec![
719 (0, tlv::TlvItemValueEnc::OctetString(schedule_handle)).into(),
720 ]),
721 };
722 Ok(tlv.encode()?)
723}
724
725pub fn encode_set_active_preset_request(preset_handle: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
727 let tlv = tlv::TlvItemEnc {
728 tag: 0,
729 value: tlv::TlvItemValueEnc::StructInvisible(vec![
730 (0, tlv::TlvItemValueEnc::OctetString(preset_handle.unwrap_or(vec![]))).into(),
731 ]),
732 };
733 Ok(tlv.encode()?)
734}
735
736pub fn decode_local_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
740 if let tlv::TlvItemValue::Int(v) = inp {
741 Ok(Some(*v as i16))
742 } else {
743 Ok(None)
744 }
745}
746
747pub fn decode_outdoor_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
749 if let tlv::TlvItemValue::Int(v) = inp {
750 Ok(Some(*v as i16))
751 } else {
752 Ok(None)
753 }
754}
755
756pub fn decode_occupancy(inp: &tlv::TlvItemValue) -> anyhow::Result<Occupancy> {
758 if let tlv::TlvItemValue::Int(v) = inp {
759 Ok(*v as u8)
760 } else {
761 Err(anyhow::anyhow!("Expected Integer"))
762 }
763}
764
765pub fn decode_abs_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
767 if let tlv::TlvItemValue::Int(v) = inp {
768 Ok(*v as i16)
769 } else {
770 Err(anyhow::anyhow!("Expected Int16"))
771 }
772}
773
774pub fn decode_abs_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
776 if let tlv::TlvItemValue::Int(v) = inp {
777 Ok(*v as i16)
778 } else {
779 Err(anyhow::anyhow!("Expected Int16"))
780 }
781}
782
783pub fn decode_abs_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
785 if let tlv::TlvItemValue::Int(v) = inp {
786 Ok(*v as i16)
787 } else {
788 Err(anyhow::anyhow!("Expected Int16"))
789 }
790}
791
792pub fn decode_abs_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
794 if let tlv::TlvItemValue::Int(v) = inp {
795 Ok(*v as i16)
796 } else {
797 Err(anyhow::anyhow!("Expected Int16"))
798 }
799}
800
801pub fn decode_pi_cooling_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
803 if let tlv::TlvItemValue::Int(v) = inp {
804 Ok(*v as u8)
805 } else {
806 Err(anyhow::anyhow!("Expected UInt8"))
807 }
808}
809
810pub fn decode_pi_heating_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
812 if let tlv::TlvItemValue::Int(v) = inp {
813 Ok(*v as u8)
814 } else {
815 Err(anyhow::anyhow!("Expected UInt8"))
816 }
817}
818
819pub fn decode_hvac_system_type_configuration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
821 if let tlv::TlvItemValue::Int(v) = inp {
822 Ok(*v as u8)
823 } else {
824 Err(anyhow::anyhow!("Expected UInt8"))
825 }
826}
827
828pub fn decode_local_temperature_calibration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
830 if let tlv::TlvItemValue::Int(v) = inp {
831 Ok(*v as u8)
832 } else {
833 Err(anyhow::anyhow!("Expected UInt8"))
834 }
835}
836
837pub fn decode_occupied_cooling_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
839 if let tlv::TlvItemValue::Int(v) = inp {
840 Ok(*v as i16)
841 } else {
842 Err(anyhow::anyhow!("Expected Int16"))
843 }
844}
845
846pub fn decode_occupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
848 if let tlv::TlvItemValue::Int(v) = inp {
849 Ok(*v as i16)
850 } else {
851 Err(anyhow::anyhow!("Expected Int16"))
852 }
853}
854
855pub fn decode_unoccupied_cooling_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
857 if let tlv::TlvItemValue::Int(v) = inp {
858 Ok(*v as i16)
859 } else {
860 Err(anyhow::anyhow!("Expected Int16"))
861 }
862}
863
864pub fn decode_unoccupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
866 if let tlv::TlvItemValue::Int(v) = inp {
867 Ok(*v as i16)
868 } else {
869 Err(anyhow::anyhow!("Expected Int16"))
870 }
871}
872
873pub fn decode_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
875 if let tlv::TlvItemValue::Int(v) = inp {
876 Ok(*v as i16)
877 } else {
878 Err(anyhow::anyhow!("Expected Int16"))
879 }
880}
881
882pub fn decode_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
884 if let tlv::TlvItemValue::Int(v) = inp {
885 Ok(*v as i16)
886 } else {
887 Err(anyhow::anyhow!("Expected Int16"))
888 }
889}
890
891pub fn decode_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
893 if let tlv::TlvItemValue::Int(v) = inp {
894 Ok(*v as i16)
895 } else {
896 Err(anyhow::anyhow!("Expected Int16"))
897 }
898}
899
900pub fn decode_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
902 if let tlv::TlvItemValue::Int(v) = inp {
903 Ok(*v as i16)
904 } else {
905 Err(anyhow::anyhow!("Expected Int16"))
906 }
907}
908
909pub fn decode_min_setpoint_dead_band(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
911 if let tlv::TlvItemValue::Int(v) = inp {
912 Ok(*v as u8)
913 } else {
914 Err(anyhow::anyhow!("Expected UInt8"))
915 }
916}
917
918pub fn decode_remote_sensing(inp: &tlv::TlvItemValue) -> anyhow::Result<RemoteSensing> {
920 if let tlv::TlvItemValue::Int(v) = inp {
921 Ok(*v as u8)
922 } else {
923 Err(anyhow::anyhow!("Expected Integer"))
924 }
925}
926
927pub fn decode_control_sequence_of_operation(inp: &tlv::TlvItemValue) -> anyhow::Result<ControlSequenceOfOperation> {
929 if let tlv::TlvItemValue::Int(v) = inp {
930 ControlSequenceOfOperation::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
931 } else {
932 Err(anyhow::anyhow!("Expected Integer"))
933 }
934}
935
936pub fn decode_system_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<SystemMode> {
938 if let tlv::TlvItemValue::Int(v) = inp {
939 SystemMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
940 } else {
941 Err(anyhow::anyhow!("Expected Integer"))
942 }
943}
944
945pub fn decode_thermostat_running_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ThermostatRunningMode> {
947 if let tlv::TlvItemValue::Int(v) = inp {
948 ThermostatRunningMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
949 } else {
950 Err(anyhow::anyhow!("Expected Integer"))
951 }
952}
953
954pub fn decode_temperature_setpoint_hold(inp: &tlv::TlvItemValue) -> anyhow::Result<TemperatureSetpointHold> {
956 if let tlv::TlvItemValue::Int(v) = inp {
957 TemperatureSetpointHold::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
958 } else {
959 Err(anyhow::anyhow!("Expected Integer"))
960 }
961}
962
963pub fn decode_temperature_setpoint_hold_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
965 if let tlv::TlvItemValue::Int(v) = inp {
966 Ok(Some(*v as u16))
967 } else {
968 Ok(None)
969 }
970}
971
972pub fn decode_thermostat_programming_operation_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ProgrammingOperationMode> {
974 if let tlv::TlvItemValue::Int(v) = inp {
975 Ok(*v as u8)
976 } else {
977 Err(anyhow::anyhow!("Expected Integer"))
978 }
979}
980
981pub fn decode_thermostat_running_state(inp: &tlv::TlvItemValue) -> anyhow::Result<RelayState> {
983 if let tlv::TlvItemValue::Int(v) = inp {
984 Ok(*v as u8)
985 } else {
986 Err(anyhow::anyhow!("Expected Integer"))
987 }
988}
989
990pub fn decode_setpoint_change_source(inp: &tlv::TlvItemValue) -> anyhow::Result<SetpointChangeSource> {
992 if let tlv::TlvItemValue::Int(v) = inp {
993 SetpointChangeSource::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
994 } else {
995 Err(anyhow::anyhow!("Expected Integer"))
996 }
997}
998
999pub fn decode_setpoint_change_amount(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
1001 if let tlv::TlvItemValue::Int(v) = inp {
1002 Ok(Some(*v as u8))
1003 } else {
1004 Ok(None)
1005 }
1006}
1007
1008pub fn decode_setpoint_change_source_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
1010 if let tlv::TlvItemValue::Int(v) = inp {
1011 Ok(*v)
1012 } else {
1013 Err(anyhow::anyhow!("Expected UInt64"))
1014 }
1015}
1016
1017pub fn decode_occupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1019 if let tlv::TlvItemValue::Int(v) = inp {
1020 Ok(*v as u8)
1021 } else {
1022 Err(anyhow::anyhow!("Expected UInt8"))
1023 }
1024}
1025
1026pub fn decode_occupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1028 if let tlv::TlvItemValue::Int(v) = inp {
1029 Ok(*v as u8)
1030 } else {
1031 Err(anyhow::anyhow!("Expected UInt8"))
1032 }
1033}
1034
1035pub fn decode_occupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1037 if let tlv::TlvItemValue::Int(v) = inp {
1038 Ok(*v as u8)
1039 } else {
1040 Err(anyhow::anyhow!("Expected UInt8"))
1041 }
1042}
1043
1044pub fn decode_unoccupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1046 if let tlv::TlvItemValue::Int(v) = inp {
1047 Ok(*v as u8)
1048 } else {
1049 Err(anyhow::anyhow!("Expected UInt8"))
1050 }
1051}
1052
1053pub fn decode_unoccupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1055 if let tlv::TlvItemValue::Int(v) = inp {
1056 Ok(*v as u8)
1057 } else {
1058 Err(anyhow::anyhow!("Expected UInt8"))
1059 }
1060}
1061
1062pub fn decode_unoccupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1064 if let tlv::TlvItemValue::Int(v) = inp {
1065 Ok(*v as u8)
1066 } else {
1067 Err(anyhow::anyhow!("Expected UInt8"))
1068 }
1069}
1070
1071pub fn decode_emergency_heat_delta(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1073 if let tlv::TlvItemValue::Int(v) = inp {
1074 Ok(*v as u8)
1075 } else {
1076 Err(anyhow::anyhow!("Expected UInt8"))
1077 }
1078}
1079
1080pub fn decode_ac_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACType> {
1082 if let tlv::TlvItemValue::Int(v) = inp {
1083 ACType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1084 } else {
1085 Err(anyhow::anyhow!("Expected Integer"))
1086 }
1087}
1088
1089pub fn decode_ac_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1091 if let tlv::TlvItemValue::Int(v) = inp {
1092 Ok(*v as u16)
1093 } else {
1094 Err(anyhow::anyhow!("Expected UInt16"))
1095 }
1096}
1097
1098pub fn decode_ac_refrigerant_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACRefrigerantType> {
1100 if let tlv::TlvItemValue::Int(v) = inp {
1101 ACRefrigerantType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1102 } else {
1103 Err(anyhow::anyhow!("Expected Integer"))
1104 }
1105}
1106
1107pub fn decode_ac_compressor_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACCompressorType> {
1109 if let tlv::TlvItemValue::Int(v) = inp {
1110 ACCompressorType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1111 } else {
1112 Err(anyhow::anyhow!("Expected Integer"))
1113 }
1114}
1115
1116pub fn decode_ac_error_code(inp: &tlv::TlvItemValue) -> anyhow::Result<ACErrorCode> {
1118 if let tlv::TlvItemValue::Int(v) = inp {
1119 Ok(*v as u8)
1120 } else {
1121 Err(anyhow::anyhow!("Expected Integer"))
1122 }
1123}
1124
1125pub fn decode_aclouver_position(inp: &tlv::TlvItemValue) -> anyhow::Result<ACLouverPosition> {
1127 if let tlv::TlvItemValue::Int(v) = inp {
1128 ACLouverPosition::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1129 } else {
1130 Err(anyhow::anyhow!("Expected Integer"))
1131 }
1132}
1133
1134pub fn decode_ac_coil_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
1136 if let tlv::TlvItemValue::Int(v) = inp {
1137 Ok(Some(*v as i16))
1138 } else {
1139 Ok(None)
1140 }
1141}
1142
1143pub fn decode_ac_capacity_format(inp: &tlv::TlvItemValue) -> anyhow::Result<ACCapacityFormat> {
1145 if let tlv::TlvItemValue::Int(v) = inp {
1146 ACCapacityFormat::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1147 } else {
1148 Err(anyhow::anyhow!("Expected Integer"))
1149 }
1150}
1151
1152pub fn decode_preset_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<PresetType>> {
1154 let mut res = Vec::new();
1155 if let tlv::TlvItemValue::List(v) = inp {
1156 for item in v {
1157 res.push(PresetType {
1158 preset_scenario: item.get_int(&[0]).and_then(|v| PresetScenario::from_u8(v as u8)),
1159 number_of_presets: item.get_int(&[1]).map(|v| v as u8),
1160 preset_type_features: item.get_int(&[2]).map(|v| v as u8),
1161 });
1162 }
1163 }
1164 Ok(res)
1165}
1166
1167pub fn decode_schedule_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ScheduleType>> {
1169 let mut res = Vec::new();
1170 if let tlv::TlvItemValue::List(v) = inp {
1171 for item in v {
1172 res.push(ScheduleType {
1173 system_mode: item.get_int(&[0]).and_then(|v| SystemMode::from_u8(v as u8)),
1174 number_of_schedules: item.get_int(&[1]).map(|v| v as u8),
1175 schedule_type_features: item.get_int(&[2]).map(|v| v as u8),
1176 });
1177 }
1178 }
1179 Ok(res)
1180}
1181
1182pub fn decode_number_of_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1184 if let tlv::TlvItemValue::Int(v) = inp {
1185 Ok(*v as u8)
1186 } else {
1187 Err(anyhow::anyhow!("Expected UInt8"))
1188 }
1189}
1190
1191pub fn decode_number_of_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1193 if let tlv::TlvItemValue::Int(v) = inp {
1194 Ok(*v as u8)
1195 } else {
1196 Err(anyhow::anyhow!("Expected UInt8"))
1197 }
1198}
1199
1200pub fn decode_number_of_schedule_transitions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1202 if let tlv::TlvItemValue::Int(v) = inp {
1203 Ok(*v as u8)
1204 } else {
1205 Err(anyhow::anyhow!("Expected UInt8"))
1206 }
1207}
1208
1209pub fn decode_number_of_schedule_transition_per_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
1211 if let tlv::TlvItemValue::Int(v) = inp {
1212 Ok(Some(*v as u8))
1213 } else {
1214 Ok(None)
1215 }
1216}
1217
1218pub fn decode_active_preset_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1220 if let tlv::TlvItemValue::OctetString(v) = inp {
1221 Ok(Some(v.clone()))
1222 } else {
1223 Ok(None)
1224 }
1225}
1226
1227pub fn decode_active_schedule_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1229 if let tlv::TlvItemValue::OctetString(v) = inp {
1230 Ok(Some(v.clone()))
1231 } else {
1232 Ok(None)
1233 }
1234}
1235
1236pub fn decode_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Preset>> {
1238 let mut res = Vec::new();
1239 if let tlv::TlvItemValue::List(v) = inp {
1240 for item in v {
1241 res.push(Preset {
1242 preset_handle: item.get_octet_string_owned(&[0]),
1243 preset_scenario: item.get_int(&[1]).and_then(|v| PresetScenario::from_u8(v as u8)),
1244 name: item.get_string_owned(&[2]),
1245 cooling_setpoint: item.get_int(&[3]).map(|v| v as i16),
1246 heating_setpoint: item.get_int(&[4]).map(|v| v as i16),
1247 built_in: item.get_bool(&[5]),
1248 });
1249 }
1250 }
1251 Ok(res)
1252}
1253
1254pub fn decode_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Schedule>> {
1256 let mut res = Vec::new();
1257 if let tlv::TlvItemValue::List(v) = inp {
1258 for item in v {
1259 res.push(Schedule {
1260 schedule_handle: item.get_octet_string_owned(&[0]),
1261 system_mode: item.get_int(&[1]).and_then(|v| SystemMode::from_u8(v as u8)),
1262 name: item.get_string_owned(&[2]),
1263 preset_handle: item.get_octet_string_owned(&[3]),
1264 transitions: {
1265 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
1266 let mut items = Vec::new();
1267 for list_item in l {
1268 items.push(ScheduleTransition {
1269 day_of_week: list_item.get_int(&[0]).map(|v| v as u8),
1270 transition_time: list_item.get_int(&[1]).map(|v| v as u16),
1271 preset_handle: list_item.get_octet_string_owned(&[2]),
1272 system_mode: list_item.get_int(&[3]).and_then(|v| SystemMode::from_u8(v as u8)),
1273 cooling_setpoint: list_item.get_int(&[4]).map(|v| v as i16),
1274 heating_setpoint: list_item.get_int(&[5]).map(|v| v as i16),
1275 });
1276 }
1277 Some(items)
1278 } else {
1279 None
1280 }
1281 },
1282 built_in: item.get_bool(&[5]),
1283 });
1284 }
1285 }
1286 Ok(res)
1287}
1288
1289pub fn decode_setpoint_hold_expiry_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
1291 if let tlv::TlvItemValue::Int(v) = inp {
1292 Ok(Some(*v))
1293 } else {
1294 Ok(None)
1295 }
1296}
1297
1298
1299pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1311 if cluster_id != 0x0201 {
1313 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0201, got {}\"}}", cluster_id);
1314 }
1315
1316 match attribute_id {
1317 0x0000 => {
1318 match decode_local_temperature(tlv_value) {
1319 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1320 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1321 }
1322 }
1323 0x0001 => {
1324 match decode_outdoor_temperature(tlv_value) {
1325 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1326 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1327 }
1328 }
1329 0x0002 => {
1330 match decode_occupancy(tlv_value) {
1331 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1332 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1333 }
1334 }
1335 0x0003 => {
1336 match decode_abs_min_heat_setpoint_limit(tlv_value) {
1337 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1338 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1339 }
1340 }
1341 0x0004 => {
1342 match decode_abs_max_heat_setpoint_limit(tlv_value) {
1343 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1344 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1345 }
1346 }
1347 0x0005 => {
1348 match decode_abs_min_cool_setpoint_limit(tlv_value) {
1349 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1350 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1351 }
1352 }
1353 0x0006 => {
1354 match decode_abs_max_cool_setpoint_limit(tlv_value) {
1355 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1356 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1357 }
1358 }
1359 0x0007 => {
1360 match decode_pi_cooling_demand(tlv_value) {
1361 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1362 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1363 }
1364 }
1365 0x0008 => {
1366 match decode_pi_heating_demand(tlv_value) {
1367 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1368 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1369 }
1370 }
1371 0x0009 => {
1372 match decode_hvac_system_type_configuration(tlv_value) {
1373 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1374 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1375 }
1376 }
1377 0x0010 => {
1378 match decode_local_temperature_calibration(tlv_value) {
1379 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1380 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1381 }
1382 }
1383 0x0011 => {
1384 match decode_occupied_cooling_setpoint(tlv_value) {
1385 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1386 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1387 }
1388 }
1389 0x0012 => {
1390 match decode_occupied_heating_setpoint(tlv_value) {
1391 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1392 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1393 }
1394 }
1395 0x0013 => {
1396 match decode_unoccupied_cooling_setpoint(tlv_value) {
1397 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1398 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1399 }
1400 }
1401 0x0014 => {
1402 match decode_unoccupied_heating_setpoint(tlv_value) {
1403 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1404 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1405 }
1406 }
1407 0x0015 => {
1408 match decode_min_heat_setpoint_limit(tlv_value) {
1409 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1410 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1411 }
1412 }
1413 0x0016 => {
1414 match decode_max_heat_setpoint_limit(tlv_value) {
1415 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1416 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1417 }
1418 }
1419 0x0017 => {
1420 match decode_min_cool_setpoint_limit(tlv_value) {
1421 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1422 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1423 }
1424 }
1425 0x0018 => {
1426 match decode_max_cool_setpoint_limit(tlv_value) {
1427 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1428 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1429 }
1430 }
1431 0x0019 => {
1432 match decode_min_setpoint_dead_band(tlv_value) {
1433 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1434 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1435 }
1436 }
1437 0x001A => {
1438 match decode_remote_sensing(tlv_value) {
1439 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1440 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1441 }
1442 }
1443 0x001B => {
1444 match decode_control_sequence_of_operation(tlv_value) {
1445 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1446 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1447 }
1448 }
1449 0x001C => {
1450 match decode_system_mode(tlv_value) {
1451 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1452 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1453 }
1454 }
1455 0x001E => {
1456 match decode_thermostat_running_mode(tlv_value) {
1457 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1458 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1459 }
1460 }
1461 0x0023 => {
1462 match decode_temperature_setpoint_hold(tlv_value) {
1463 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1464 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1465 }
1466 }
1467 0x0024 => {
1468 match decode_temperature_setpoint_hold_duration(tlv_value) {
1469 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1470 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1471 }
1472 }
1473 0x0025 => {
1474 match decode_thermostat_programming_operation_mode(tlv_value) {
1475 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1476 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1477 }
1478 }
1479 0x0029 => {
1480 match decode_thermostat_running_state(tlv_value) {
1481 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1482 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1483 }
1484 }
1485 0x0030 => {
1486 match decode_setpoint_change_source(tlv_value) {
1487 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1488 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1489 }
1490 }
1491 0x0031 => {
1492 match decode_setpoint_change_amount(tlv_value) {
1493 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1494 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1495 }
1496 }
1497 0x0032 => {
1498 match decode_setpoint_change_source_timestamp(tlv_value) {
1499 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1500 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1501 }
1502 }
1503 0x0034 => {
1504 match decode_occupied_setback(tlv_value) {
1505 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1506 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1507 }
1508 }
1509 0x0035 => {
1510 match decode_occupied_setback_min(tlv_value) {
1511 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1512 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1513 }
1514 }
1515 0x0036 => {
1516 match decode_occupied_setback_max(tlv_value) {
1517 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1518 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1519 }
1520 }
1521 0x0037 => {
1522 match decode_unoccupied_setback(tlv_value) {
1523 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1524 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1525 }
1526 }
1527 0x0038 => {
1528 match decode_unoccupied_setback_min(tlv_value) {
1529 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1530 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1531 }
1532 }
1533 0x0039 => {
1534 match decode_unoccupied_setback_max(tlv_value) {
1535 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1536 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1537 }
1538 }
1539 0x003A => {
1540 match decode_emergency_heat_delta(tlv_value) {
1541 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1542 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1543 }
1544 }
1545 0x0040 => {
1546 match decode_ac_type(tlv_value) {
1547 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1548 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1549 }
1550 }
1551 0x0041 => {
1552 match decode_ac_capacity(tlv_value) {
1553 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1554 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1555 }
1556 }
1557 0x0042 => {
1558 match decode_ac_refrigerant_type(tlv_value) {
1559 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1560 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1561 }
1562 }
1563 0x0043 => {
1564 match decode_ac_compressor_type(tlv_value) {
1565 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1566 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1567 }
1568 }
1569 0x0044 => {
1570 match decode_ac_error_code(tlv_value) {
1571 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1572 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1573 }
1574 }
1575 0x0045 => {
1576 match decode_aclouver_position(tlv_value) {
1577 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1578 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1579 }
1580 }
1581 0x0046 => {
1582 match decode_ac_coil_temperature(tlv_value) {
1583 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1584 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1585 }
1586 }
1587 0x0047 => {
1588 match decode_ac_capacity_format(tlv_value) {
1589 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1590 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1591 }
1592 }
1593 0x0048 => {
1594 match decode_preset_types(tlv_value) {
1595 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1596 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1597 }
1598 }
1599 0x0049 => {
1600 match decode_schedule_types(tlv_value) {
1601 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1602 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1603 }
1604 }
1605 0x004A => {
1606 match decode_number_of_presets(tlv_value) {
1607 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1608 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1609 }
1610 }
1611 0x004B => {
1612 match decode_number_of_schedules(tlv_value) {
1613 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1614 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1615 }
1616 }
1617 0x004C => {
1618 match decode_number_of_schedule_transitions(tlv_value) {
1619 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1620 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1621 }
1622 }
1623 0x004D => {
1624 match decode_number_of_schedule_transition_per_day(tlv_value) {
1625 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1626 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1627 }
1628 }
1629 0x004E => {
1630 match decode_active_preset_handle(tlv_value) {
1631 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1632 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1633 }
1634 }
1635 0x004F => {
1636 match decode_active_schedule_handle(tlv_value) {
1637 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1638 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1639 }
1640 }
1641 0x0050 => {
1642 match decode_presets(tlv_value) {
1643 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1644 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1645 }
1646 }
1647 0x0051 => {
1648 match decode_schedules(tlv_value) {
1649 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1650 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1651 }
1652 }
1653 0x0052 => {
1654 match decode_setpoint_hold_expiry_timestamp(tlv_value) {
1655 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1656 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1657 }
1658 }
1659 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
1660 }
1661}
1662
1663pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
1668 vec![
1669 (0x0000, "LocalTemperature"),
1670 (0x0001, "OutdoorTemperature"),
1671 (0x0002, "Occupancy"),
1672 (0x0003, "AbsMinHeatSetpointLimit"),
1673 (0x0004, "AbsMaxHeatSetpointLimit"),
1674 (0x0005, "AbsMinCoolSetpointLimit"),
1675 (0x0006, "AbsMaxCoolSetpointLimit"),
1676 (0x0007, "PICoolingDemand"),
1677 (0x0008, "PIHeatingDemand"),
1678 (0x0009, "HVACSystemTypeConfiguration"),
1679 (0x0010, "LocalTemperatureCalibration"),
1680 (0x0011, "OccupiedCoolingSetpoint"),
1681 (0x0012, "OccupiedHeatingSetpoint"),
1682 (0x0013, "UnoccupiedCoolingSetpoint"),
1683 (0x0014, "UnoccupiedHeatingSetpoint"),
1684 (0x0015, "MinHeatSetpointLimit"),
1685 (0x0016, "MaxHeatSetpointLimit"),
1686 (0x0017, "MinCoolSetpointLimit"),
1687 (0x0018, "MaxCoolSetpointLimit"),
1688 (0x0019, "MinSetpointDeadBand"),
1689 (0x001A, "RemoteSensing"),
1690 (0x001B, "ControlSequenceOfOperation"),
1691 (0x001C, "SystemMode"),
1692 (0x001E, "ThermostatRunningMode"),
1693 (0x0023, "TemperatureSetpointHold"),
1694 (0x0024, "TemperatureSetpointHoldDuration"),
1695 (0x0025, "ThermostatProgrammingOperationMode"),
1696 (0x0029, "ThermostatRunningState"),
1697 (0x0030, "SetpointChangeSource"),
1698 (0x0031, "SetpointChangeAmount"),
1699 (0x0032, "SetpointChangeSourceTimestamp"),
1700 (0x0034, "OccupiedSetback"),
1701 (0x0035, "OccupiedSetbackMin"),
1702 (0x0036, "OccupiedSetbackMax"),
1703 (0x0037, "UnoccupiedSetback"),
1704 (0x0038, "UnoccupiedSetbackMin"),
1705 (0x0039, "UnoccupiedSetbackMax"),
1706 (0x003A, "EmergencyHeatDelta"),
1707 (0x0040, "ACType"),
1708 (0x0041, "ACCapacity"),
1709 (0x0042, "ACRefrigerantType"),
1710 (0x0043, "ACCompressorType"),
1711 (0x0044, "ACErrorCode"),
1712 (0x0045, "ACLouverPosition"),
1713 (0x0046, "ACCoilTemperature"),
1714 (0x0047, "ACCapacityFormat"),
1715 (0x0048, "PresetTypes"),
1716 (0x0049, "ScheduleTypes"),
1717 (0x004A, "NumberOfPresets"),
1718 (0x004B, "NumberOfSchedules"),
1719 (0x004C, "NumberOfScheduleTransitions"),
1720 (0x004D, "NumberOfScheduleTransitionPerDay"),
1721 (0x004E, "ActivePresetHandle"),
1722 (0x004F, "ActiveScheduleHandle"),
1723 (0x0050, "Presets"),
1724 (0x0051, "Schedules"),
1725 (0x0052, "SetpointHoldExpiryTimestamp"),
1726 ]
1727}
1728