1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum ACCapacityFormat {
21 Btuh = 0,
23}
24
25impl ACCapacityFormat {
26 pub fn from_u8(value: u8) -> Option<Self> {
28 match value {
29 0 => Some(ACCapacityFormat::Btuh),
30 _ => None,
31 }
32 }
33
34 pub fn to_u8(self) -> u8 {
36 self as u8
37 }
38}
39
40impl From<ACCapacityFormat> for u8 {
41 fn from(val: ACCapacityFormat) -> Self {
42 val as u8
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
47#[repr(u8)]
48pub enum ACCompressorType {
49 Unknown = 0,
51 T1 = 1,
53 T2 = 2,
55 T3 = 3,
57}
58
59impl ACCompressorType {
60 pub fn from_u8(value: u8) -> Option<Self> {
62 match value {
63 0 => Some(ACCompressorType::Unknown),
64 1 => Some(ACCompressorType::T1),
65 2 => Some(ACCompressorType::T2),
66 3 => Some(ACCompressorType::T3),
67 _ => None,
68 }
69 }
70
71 pub fn to_u8(self) -> u8 {
73 self as u8
74 }
75}
76
77impl From<ACCompressorType> for u8 {
78 fn from(val: ACCompressorType) -> Self {
79 val as u8
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[repr(u8)]
85pub enum ACLouverPosition {
86 Closed = 1,
88 Open = 2,
90 Quarter = 3,
92 Half = 4,
94 Threequarters = 5,
96}
97
98impl ACLouverPosition {
99 pub fn from_u8(value: u8) -> Option<Self> {
101 match value {
102 1 => Some(ACLouverPosition::Closed),
103 2 => Some(ACLouverPosition::Open),
104 3 => Some(ACLouverPosition::Quarter),
105 4 => Some(ACLouverPosition::Half),
106 5 => Some(ACLouverPosition::Threequarters),
107 _ => None,
108 }
109 }
110
111 pub fn to_u8(self) -> u8 {
113 self as u8
114 }
115}
116
117impl From<ACLouverPosition> for u8 {
118 fn from(val: ACLouverPosition) -> Self {
119 val as u8
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
124#[repr(u8)]
125pub enum ACRefrigerantType {
126 Unknown = 0,
128 R22 = 1,
130 R410a = 2,
132 R407c = 3,
134}
135
136impl ACRefrigerantType {
137 pub fn from_u8(value: u8) -> Option<Self> {
139 match value {
140 0 => Some(ACRefrigerantType::Unknown),
141 1 => Some(ACRefrigerantType::R22),
142 2 => Some(ACRefrigerantType::R410a),
143 3 => Some(ACRefrigerantType::R407c),
144 _ => None,
145 }
146 }
147
148 pub fn to_u8(self) -> u8 {
150 self as u8
151 }
152}
153
154impl From<ACRefrigerantType> for u8 {
155 fn from(val: ACRefrigerantType) -> Self {
156 val as u8
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
161#[repr(u8)]
162pub enum ACType {
163 Unknown = 0,
165 Coolingfixed = 1,
167 Heatpumpfixed = 2,
169 Coolinginverter = 3,
171 Heatpumpinverter = 4,
173}
174
175impl ACType {
176 pub fn from_u8(value: u8) -> Option<Self> {
178 match value {
179 0 => Some(ACType::Unknown),
180 1 => Some(ACType::Coolingfixed),
181 2 => Some(ACType::Heatpumpfixed),
182 3 => Some(ACType::Coolinginverter),
183 4 => Some(ACType::Heatpumpinverter),
184 _ => None,
185 }
186 }
187
188 pub fn to_u8(self) -> u8 {
190 self as u8
191 }
192}
193
194impl From<ACType> for u8 {
195 fn from(val: ACType) -> Self {
196 val as u8
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
201#[repr(u8)]
202pub enum ControlSequenceOfOperation {
203 Coolingonly = 0,
205 Coolingwithreheat = 1,
207 Heatingonly = 2,
209 Heatingwithreheat = 3,
211 Coolingandheating = 4,
213 Coolingandheatingwithreheat = 5,
215}
216
217impl ControlSequenceOfOperation {
218 pub fn from_u8(value: u8) -> Option<Self> {
220 match value {
221 0 => Some(ControlSequenceOfOperation::Coolingonly),
222 1 => Some(ControlSequenceOfOperation::Coolingwithreheat),
223 2 => Some(ControlSequenceOfOperation::Heatingonly),
224 3 => Some(ControlSequenceOfOperation::Heatingwithreheat),
225 4 => Some(ControlSequenceOfOperation::Coolingandheating),
226 5 => Some(ControlSequenceOfOperation::Coolingandheatingwithreheat),
227 _ => None,
228 }
229 }
230
231 pub fn to_u8(self) -> u8 {
233 self as u8
234 }
235}
236
237impl From<ControlSequenceOfOperation> for u8 {
238 fn from(val: ControlSequenceOfOperation) -> Self {
239 val as u8
240 }
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
244#[repr(u8)]
245pub enum PresetScenario {
246 Occupied = 1,
248 Unoccupied = 2,
250 Sleep = 3,
252 Wake = 4,
254 Vacation = 5,
256 Goingtosleep = 6,
258 Userdefined = 254,
260}
261
262impl PresetScenario {
263 pub fn from_u8(value: u8) -> Option<Self> {
265 match value {
266 1 => Some(PresetScenario::Occupied),
267 2 => Some(PresetScenario::Unoccupied),
268 3 => Some(PresetScenario::Sleep),
269 4 => Some(PresetScenario::Wake),
270 5 => Some(PresetScenario::Vacation),
271 6 => Some(PresetScenario::Goingtosleep),
272 254 => Some(PresetScenario::Userdefined),
273 _ => None,
274 }
275 }
276
277 pub fn to_u8(self) -> u8 {
279 self as u8
280 }
281}
282
283impl From<PresetScenario> for u8 {
284 fn from(val: PresetScenario) -> Self {
285 val as u8
286 }
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
290#[repr(u8)]
291pub enum SetpointChangeSource {
292 Manual = 0,
294 Schedule = 1,
296 External = 2,
298}
299
300impl SetpointChangeSource {
301 pub fn from_u8(value: u8) -> Option<Self> {
303 match value {
304 0 => Some(SetpointChangeSource::Manual),
305 1 => Some(SetpointChangeSource::Schedule),
306 2 => Some(SetpointChangeSource::External),
307 _ => None,
308 }
309 }
310
311 pub fn to_u8(self) -> u8 {
313 self as u8
314 }
315}
316
317impl From<SetpointChangeSource> for u8 {
318 fn from(val: SetpointChangeSource) -> Self {
319 val as u8
320 }
321}
322
323#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
324#[repr(u8)]
325pub enum SetpointRaiseLowerMode {
326 Heat = 0,
328 Cool = 1,
330 Both = 2,
332}
333
334impl SetpointRaiseLowerMode {
335 pub fn from_u8(value: u8) -> Option<Self> {
337 match value {
338 0 => Some(SetpointRaiseLowerMode::Heat),
339 1 => Some(SetpointRaiseLowerMode::Cool),
340 2 => Some(SetpointRaiseLowerMode::Both),
341 _ => None,
342 }
343 }
344
345 pub fn to_u8(self) -> u8 {
347 self as u8
348 }
349}
350
351impl From<SetpointRaiseLowerMode> for u8 {
352 fn from(val: SetpointRaiseLowerMode) -> Self {
353 val as u8
354 }
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
358#[repr(u8)]
359pub enum StartOfWeek {
360 Sunday = 0,
361 Monday = 1,
362 Tuesday = 2,
363 Wednesday = 3,
364 Thursday = 4,
365 Friday = 5,
366 Saturday = 6,
367}
368
369impl StartOfWeek {
370 pub fn from_u8(value: u8) -> Option<Self> {
372 match value {
373 0 => Some(StartOfWeek::Sunday),
374 1 => Some(StartOfWeek::Monday),
375 2 => Some(StartOfWeek::Tuesday),
376 3 => Some(StartOfWeek::Wednesday),
377 4 => Some(StartOfWeek::Thursday),
378 5 => Some(StartOfWeek::Friday),
379 6 => Some(StartOfWeek::Saturday),
380 _ => None,
381 }
382 }
383
384 pub fn to_u8(self) -> u8 {
386 self as u8
387 }
388}
389
390impl From<StartOfWeek> for u8 {
391 fn from(val: StartOfWeek) -> Self {
392 val as u8
393 }
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
397#[repr(u8)]
398pub enum SystemMode {
399 Off = 0,
401 Auto = 1,
403 Cool = 3,
405 Heat = 4,
407 Emergencyheat = 5,
409 Precooling = 6,
411 Fanonly = 7,
412 Dry = 8,
413 Sleep = 9,
414}
415
416impl SystemMode {
417 pub fn from_u8(value: u8) -> Option<Self> {
419 match value {
420 0 => Some(SystemMode::Off),
421 1 => Some(SystemMode::Auto),
422 3 => Some(SystemMode::Cool),
423 4 => Some(SystemMode::Heat),
424 5 => Some(SystemMode::Emergencyheat),
425 6 => Some(SystemMode::Precooling),
426 7 => Some(SystemMode::Fanonly),
427 8 => Some(SystemMode::Dry),
428 9 => Some(SystemMode::Sleep),
429 _ => None,
430 }
431 }
432
433 pub fn to_u8(self) -> u8 {
435 self as u8
436 }
437}
438
439impl From<SystemMode> for u8 {
440 fn from(val: SystemMode) -> Self {
441 val as u8
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
446#[repr(u8)]
447pub enum TemperatureSetpointHold {
448 Setpointholdoff = 0,
450 Setpointholdon = 1,
452}
453
454impl TemperatureSetpointHold {
455 pub fn from_u8(value: u8) -> Option<Self> {
457 match value {
458 0 => Some(TemperatureSetpointHold::Setpointholdoff),
459 1 => Some(TemperatureSetpointHold::Setpointholdon),
460 _ => None,
461 }
462 }
463
464 pub fn to_u8(self) -> u8 {
466 self as u8
467 }
468}
469
470impl From<TemperatureSetpointHold> for u8 {
471 fn from(val: TemperatureSetpointHold) -> Self {
472 val as u8
473 }
474}
475
476#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
477#[repr(u8)]
478pub enum ThermostatRunningMode {
479 Off = 0,
481 Cool = 3,
483 Heat = 4,
485}
486
487impl ThermostatRunningMode {
488 pub fn from_u8(value: u8) -> Option<Self> {
490 match value {
491 0 => Some(ThermostatRunningMode::Off),
492 3 => Some(ThermostatRunningMode::Cool),
493 4 => Some(ThermostatRunningMode::Heat),
494 _ => None,
495 }
496 }
497
498 pub fn to_u8(self) -> u8 {
500 self as u8
501 }
502}
503
504impl From<ThermostatRunningMode> for u8 {
505 fn from(val: ThermostatRunningMode) -> Self {
506 val as u8
507 }
508}
509
510pub type ACErrorCode = u8;
514
515pub mod acerrorcode {
517 pub const COMPRESSOR_FAIL: u8 = 0x01;
519 pub const ROOM_SENSOR_FAIL: u8 = 0x02;
521 pub const OUTDOOR_SENSOR_FAIL: u8 = 0x04;
523 pub const COIL_SENSOR_FAIL: u8 = 0x08;
525 pub const FAN_FAIL: u8 = 0x10;
527}
528
529pub type Occupancy = u8;
531
532pub mod occupancy {
534 pub const OCCUPIED: u8 = 0x01;
536}
537
538pub type PresetTypeFeatures = u8;
540
541pub mod presettypefeatures {
543 pub const AUTOMATIC: u8 = 0x01;
545 pub const SUPPORTS_NAMES: u8 = 0x02;
547}
548
549pub type RelayState = u8;
551
552pub mod relaystate {
554 pub const HEAT: u8 = 0x01;
556 pub const COOL: u8 = 0x02;
558 pub const FAN: u8 = 0x04;
560 pub const HEAT_STAGE2: u8 = 0x08;
562 pub const COOL_STAGE2: u8 = 0x10;
564 pub const FAN_STAGE2: u8 = 0x20;
566 pub const FAN_STAGE3: u8 = 0x40;
568}
569
570pub type RemoteSensing = u8;
572
573pub mod remotesensing {
575 pub const LOCAL_TEMPERATURE: u8 = 0x01;
577 pub const OUTDOOR_TEMPERATURE: u8 = 0x02;
579 pub const OCCUPANCY: u8 = 0x04;
581}
582
583pub type ScheduleDayOfWeek = u8;
585
586pub mod scheduledayofweek {
588 pub const SUNDAY: u8 = 0x01;
590 pub const MONDAY: u8 = 0x02;
592 pub const TUESDAY: u8 = 0x04;
594 pub const WEDNESDAY: u8 = 0x08;
596 pub const THURSDAY: u8 = 0x10;
598 pub const FRIDAY: u8 = 0x20;
600 pub const SATURDAY: u8 = 0x40;
602 pub const AWAY: u8 = 0x80;
604}
605
606pub type ScheduleMode = u8;
608
609pub mod schedulemode {
611 pub const HEAT_SETPOINT_PRESENT: u8 = 0x01;
613 pub const COOL_SETPOINT_PRESENT: u8 = 0x02;
615}
616
617pub type ScheduleTypeFeatures = u8;
619
620pub mod scheduletypefeatures {
622 pub const SUPPORTS_PRESETS: u8 = 0x01;
624 pub const SUPPORTS_SETPOINTS: u8 = 0x02;
626 pub const SUPPORTS_NAMES: u8 = 0x04;
628 pub const SUPPORTS_OFF: u8 = 0x08;
630}
631
632pub type ThermostatSuggestionNotFollowingReason = u8;
634
635pub mod thermostatsuggestionnotfollowingreason {
637 pub const DEMAND_RESPONSE_EVENT: u8 = 0x01;
639 pub const ONGOING_HOLD: u8 = 0x02;
641 pub const SCHEDULE: u8 = 0x04;
643 pub const OCCUPANCY: u8 = 0x08;
645 pub const VACATION_MODE: u8 = 0x10;
647 pub const TIME_OF_USE_COST_SAVINGS: u8 = 0x20;
649 pub const PRE_COOLING_OR_PRE_HEATING: u8 = 0x40;
651 pub const CONFLICTING_SUGGESTIONS: u8 = 0x80;
653}
654
655#[derive(Debug, serde::Serialize)]
658pub struct Preset {
659 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
660 pub preset_handle: Option<Vec<u8>>,
661 pub preset_scenario: Option<PresetScenario>,
662 pub name: Option<String>,
663 pub cooling_setpoint: Option<i16>,
664 pub heating_setpoint: Option<i16>,
665 pub built_in: Option<bool>,
666}
667
668#[derive(Debug, serde::Serialize)]
669pub struct PresetType {
670 pub preset_scenario: Option<PresetScenario>,
671 pub number_of_presets: Option<u8>,
672 pub preset_type_features: Option<PresetTypeFeatures>,
673}
674
675#[derive(Debug, serde::Serialize)]
676pub struct Schedule {
677 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
678 pub schedule_handle: Option<Vec<u8>>,
679 pub system_mode: Option<SystemMode>,
680 pub name: Option<String>,
681 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
682 pub preset_handle: Option<Vec<u8>>,
683 pub transitions: Option<Vec<ScheduleTransition>>,
684 pub built_in: Option<bool>,
685}
686
687#[derive(Debug, serde::Serialize)]
688pub struct ScheduleTransition {
689 pub day_of_week: Option<ScheduleDayOfWeek>,
690 pub transition_time: Option<u16>,
691 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
692 pub preset_handle: Option<Vec<u8>>,
693 pub system_mode: Option<SystemMode>,
694 pub cooling_setpoint: Option<i16>,
695 pub heating_setpoint: Option<i16>,
696}
697
698#[derive(Debug, serde::Serialize)]
699pub struct ScheduleType {
700 pub system_mode: Option<SystemMode>,
701 pub number_of_schedules: Option<u8>,
702 pub schedule_type_features: Option<ScheduleTypeFeatures>,
703}
704
705#[derive(Debug, serde::Serialize)]
706pub struct ThermostatSuggestion {
707 pub unique_id: Option<u8>,
708 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
709 pub preset_handle: Option<Vec<u8>>,
710 pub effective_time: Option<u64>,
711 pub expiration_time: Option<u64>,
712}
713
714#[derive(Debug, serde::Serialize)]
715pub struct WeeklyScheduleTransition {
716 pub transition_time: Option<u16>,
717 pub heat_setpoint: Option<i16>,
718 pub cool_setpoint: Option<i16>,
719}
720
721pub fn encode_setpoint_raise_lower(mode: SetpointRaiseLowerMode, amount: i8) -> anyhow::Result<Vec<u8>> {
725 let tlv = tlv::TlvItemEnc {
726 tag: 0,
727 value: tlv::TlvItemValueEnc::StructInvisible(vec![
728 (0, tlv::TlvItemValueEnc::UInt8(mode.to_u8())).into(),
729 (1, tlv::TlvItemValueEnc::Int8(amount)).into(),
730 ]),
731 };
732 Ok(tlv.encode()?)
733}
734
735pub fn encode_set_active_schedule_request(schedule_handle: Vec<u8>) -> anyhow::Result<Vec<u8>> {
737 let tlv = tlv::TlvItemEnc {
738 tag: 0,
739 value: tlv::TlvItemValueEnc::StructInvisible(vec![
740 (0, tlv::TlvItemValueEnc::OctetString(schedule_handle)).into(),
741 ]),
742 };
743 Ok(tlv.encode()?)
744}
745
746pub fn encode_set_active_preset_request(preset_handle: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
748 let tlv = tlv::TlvItemEnc {
749 tag: 0,
750 value: tlv::TlvItemValueEnc::StructInvisible(vec![
751 (0, tlv::TlvItemValueEnc::OctetString(preset_handle.unwrap_or(vec![]))).into(),
752 ]),
753 };
754 Ok(tlv.encode()?)
755}
756
757pub fn encode_add_thermostat_suggestion(preset_handle: Vec<u8>, effective_time: Option<u64>, expiration_in_minutes: u16) -> anyhow::Result<Vec<u8>> {
759 let tlv = tlv::TlvItemEnc {
760 tag: 0,
761 value: tlv::TlvItemValueEnc::StructInvisible(vec![
762 (0, tlv::TlvItemValueEnc::OctetString(preset_handle)).into(),
763 (1, tlv::TlvItemValueEnc::UInt64(effective_time.unwrap_or(0))).into(),
764 (2, tlv::TlvItemValueEnc::UInt16(expiration_in_minutes)).into(),
765 ]),
766 };
767 Ok(tlv.encode()?)
768}
769
770pub fn encode_remove_thermostat_suggestion(unique_id: u8) -> anyhow::Result<Vec<u8>> {
772 let tlv = tlv::TlvItemEnc {
773 tag: 0,
774 value: tlv::TlvItemValueEnc::StructInvisible(vec![
775 (0, tlv::TlvItemValueEnc::UInt8(unique_id)).into(),
776 ]),
777 };
778 Ok(tlv.encode()?)
779}
780
781pub fn decode_local_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
785 if let tlv::TlvItemValue::Int(v) = inp {
786 Ok(Some(*v as i16))
787 } else {
788 Ok(None)
789 }
790}
791
792pub fn decode_outdoor_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
794 if let tlv::TlvItemValue::Int(v) = inp {
795 Ok(Some(*v as i16))
796 } else {
797 Ok(None)
798 }
799}
800
801pub fn decode_occupancy(inp: &tlv::TlvItemValue) -> anyhow::Result<Occupancy> {
803 if let tlv::TlvItemValue::Int(v) = inp {
804 Ok(*v as u8)
805 } else {
806 Err(anyhow::anyhow!("Expected Integer"))
807 }
808}
809
810pub fn decode_abs_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
812 if let tlv::TlvItemValue::Int(v) = inp {
813 Ok(*v as i16)
814 } else {
815 Err(anyhow::anyhow!("Expected Int16"))
816 }
817}
818
819pub fn decode_abs_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
821 if let tlv::TlvItemValue::Int(v) = inp {
822 Ok(*v as i16)
823 } else {
824 Err(anyhow::anyhow!("Expected Int16"))
825 }
826}
827
828pub fn decode_abs_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
830 if let tlv::TlvItemValue::Int(v) = inp {
831 Ok(*v as i16)
832 } else {
833 Err(anyhow::anyhow!("Expected Int16"))
834 }
835}
836
837pub fn decode_abs_max_cool_setpoint_limit(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_pi_cooling_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
848 if let tlv::TlvItemValue::Int(v) = inp {
849 Ok(*v as u8)
850 } else {
851 Err(anyhow::anyhow!("Expected UInt8"))
852 }
853}
854
855pub fn decode_pi_heating_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
857 if let tlv::TlvItemValue::Int(v) = inp {
858 Ok(*v as u8)
859 } else {
860 Err(anyhow::anyhow!("Expected UInt8"))
861 }
862}
863
864pub fn decode_hvac_system_type_configuration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
866 if let tlv::TlvItemValue::Int(v) = inp {
867 Ok(*v as u8)
868 } else {
869 Err(anyhow::anyhow!("Expected UInt8"))
870 }
871}
872
873pub fn decode_local_temperature_calibration(inp: &tlv::TlvItemValue) -> anyhow::Result<i8> {
875 if let tlv::TlvItemValue::Int(v) = inp {
876 Ok(*v as i8)
877 } else {
878 Err(anyhow::anyhow!("Expected Int8"))
879 }
880}
881
882pub fn decode_occupied_cooling_setpoint(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_occupied_heating_setpoint(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_unoccupied_cooling_setpoint(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_unoccupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
911 if let tlv::TlvItemValue::Int(v) = inp {
912 Ok(*v as i16)
913 } else {
914 Err(anyhow::anyhow!("Expected Int16"))
915 }
916}
917
918pub fn decode_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
920 if let tlv::TlvItemValue::Int(v) = inp {
921 Ok(*v as i16)
922 } else {
923 Err(anyhow::anyhow!("Expected Int16"))
924 }
925}
926
927pub fn decode_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
929 if let tlv::TlvItemValue::Int(v) = inp {
930 Ok(*v as i16)
931 } else {
932 Err(anyhow::anyhow!("Expected Int16"))
933 }
934}
935
936pub fn decode_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
938 if let tlv::TlvItemValue::Int(v) = inp {
939 Ok(*v as i16)
940 } else {
941 Err(anyhow::anyhow!("Expected Int16"))
942 }
943}
944
945pub fn decode_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
947 if let tlv::TlvItemValue::Int(v) = inp {
948 Ok(*v as i16)
949 } else {
950 Err(anyhow::anyhow!("Expected Int16"))
951 }
952}
953
954pub fn decode_min_setpoint_dead_band(inp: &tlv::TlvItemValue) -> anyhow::Result<i8> {
956 if let tlv::TlvItemValue::Int(v) = inp {
957 Ok(*v as i8)
958 } else {
959 Err(anyhow::anyhow!("Expected Int8"))
960 }
961}
962
963pub fn decode_remote_sensing(inp: &tlv::TlvItemValue) -> anyhow::Result<RemoteSensing> {
965 if let tlv::TlvItemValue::Int(v) = inp {
966 Ok(*v as u8)
967 } else {
968 Err(anyhow::anyhow!("Expected Integer"))
969 }
970}
971
972pub fn decode_control_sequence_of_operation(inp: &tlv::TlvItemValue) -> anyhow::Result<ControlSequenceOfOperation> {
974 if let tlv::TlvItemValue::Int(v) = inp {
975 ControlSequenceOfOperation::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
976 } else {
977 Err(anyhow::anyhow!("Expected Integer"))
978 }
979}
980
981pub fn decode_system_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<SystemMode> {
983 if let tlv::TlvItemValue::Int(v) = inp {
984 SystemMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
985 } else {
986 Err(anyhow::anyhow!("Expected Integer"))
987 }
988}
989
990pub fn decode_thermostat_running_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ThermostatRunningMode> {
992 if let tlv::TlvItemValue::Int(v) = inp {
993 ThermostatRunningMode::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_temperature_setpoint_hold(inp: &tlv::TlvItemValue) -> anyhow::Result<TemperatureSetpointHold> {
1001 if let tlv::TlvItemValue::Int(v) = inp {
1002 TemperatureSetpointHold::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1003 } else {
1004 Err(anyhow::anyhow!("Expected Integer"))
1005 }
1006}
1007
1008pub fn decode_temperature_setpoint_hold_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
1010 if let tlv::TlvItemValue::Int(v) = inp {
1011 Ok(Some(*v as u16))
1012 } else {
1013 Ok(None)
1014 }
1015}
1016
1017pub fn decode_thermostat_programming_operation_mode(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_thermostat_running_state(inp: &tlv::TlvItemValue) -> anyhow::Result<RelayState> {
1028 if let tlv::TlvItemValue::Int(v) = inp {
1029 Ok(*v as u8)
1030 } else {
1031 Err(anyhow::anyhow!("Expected Integer"))
1032 }
1033}
1034
1035pub fn decode_setpoint_change_source(inp: &tlv::TlvItemValue) -> anyhow::Result<SetpointChangeSource> {
1037 if let tlv::TlvItemValue::Int(v) = inp {
1038 SetpointChangeSource::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1039 } else {
1040 Err(anyhow::anyhow!("Expected Integer"))
1041 }
1042}
1043
1044pub fn decode_setpoint_change_amount(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
1046 if let tlv::TlvItemValue::Int(v) = inp {
1047 Ok(Some(*v as u8))
1048 } else {
1049 Ok(None)
1050 }
1051}
1052
1053pub fn decode_setpoint_change_source_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
1055 if let tlv::TlvItemValue::Int(v) = inp {
1056 Ok(*v)
1057 } else {
1058 Err(anyhow::anyhow!("Expected UInt64"))
1059 }
1060}
1061
1062pub fn decode_occupied_setback(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_occupied_setback_min(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_occupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1082 if let tlv::TlvItemValue::Int(v) = inp {
1083 Ok(*v as u8)
1084 } else {
1085 Err(anyhow::anyhow!("Expected UInt8"))
1086 }
1087}
1088
1089pub fn decode_unoccupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1091 if let tlv::TlvItemValue::Int(v) = inp {
1092 Ok(*v as u8)
1093 } else {
1094 Err(anyhow::anyhow!("Expected UInt8"))
1095 }
1096}
1097
1098pub fn decode_unoccupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1100 if let tlv::TlvItemValue::Int(v) = inp {
1101 Ok(*v as u8)
1102 } else {
1103 Err(anyhow::anyhow!("Expected UInt8"))
1104 }
1105}
1106
1107pub fn decode_unoccupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1109 if let tlv::TlvItemValue::Int(v) = inp {
1110 Ok(*v as u8)
1111 } else {
1112 Err(anyhow::anyhow!("Expected UInt8"))
1113 }
1114}
1115
1116pub fn decode_emergency_heat_delta(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1118 if let tlv::TlvItemValue::Int(v) = inp {
1119 Ok(*v as u8)
1120 } else {
1121 Err(anyhow::anyhow!("Expected UInt8"))
1122 }
1123}
1124
1125pub fn decode_ac_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACType> {
1127 if let tlv::TlvItemValue::Int(v) = inp {
1128 ACType::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_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
1136 if let tlv::TlvItemValue::Int(v) = inp {
1137 Ok(*v as u16)
1138 } else {
1139 Err(anyhow::anyhow!("Expected UInt16"))
1140 }
1141}
1142
1143pub fn decode_ac_refrigerant_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACRefrigerantType> {
1145 if let tlv::TlvItemValue::Int(v) = inp {
1146 ACRefrigerantType::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_ac_compressor_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACCompressorType> {
1154 if let tlv::TlvItemValue::Int(v) = inp {
1155 ACCompressorType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1156 } else {
1157 Err(anyhow::anyhow!("Expected Integer"))
1158 }
1159}
1160
1161pub fn decode_ac_error_code(inp: &tlv::TlvItemValue) -> anyhow::Result<ACErrorCode> {
1163 if let tlv::TlvItemValue::Int(v) = inp {
1164 Ok(*v as u8)
1165 } else {
1166 Err(anyhow::anyhow!("Expected Integer"))
1167 }
1168}
1169
1170pub fn decode_aclouver_position(inp: &tlv::TlvItemValue) -> anyhow::Result<ACLouverPosition> {
1172 if let tlv::TlvItemValue::Int(v) = inp {
1173 ACLouverPosition::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1174 } else {
1175 Err(anyhow::anyhow!("Expected Integer"))
1176 }
1177}
1178
1179pub fn decode_ac_coil_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
1181 if let tlv::TlvItemValue::Int(v) = inp {
1182 Ok(Some(*v as i16))
1183 } else {
1184 Ok(None)
1185 }
1186}
1187
1188pub fn decode_ac_capacity_format(inp: &tlv::TlvItemValue) -> anyhow::Result<ACCapacityFormat> {
1190 if let tlv::TlvItemValue::Int(v) = inp {
1191 ACCapacityFormat::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
1192 } else {
1193 Err(anyhow::anyhow!("Expected Integer"))
1194 }
1195}
1196
1197pub fn decode_preset_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<PresetType>> {
1199 let mut res = Vec::new();
1200 if let tlv::TlvItemValue::List(v) = inp {
1201 for item in v {
1202 res.push(PresetType {
1203 preset_scenario: item.get_int(&[0]).and_then(|v| PresetScenario::from_u8(v as u8)),
1204 number_of_presets: item.get_int(&[1]).map(|v| v as u8),
1205 preset_type_features: item.get_int(&[2]).map(|v| v as u8),
1206 });
1207 }
1208 }
1209 Ok(res)
1210}
1211
1212pub fn decode_schedule_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ScheduleType>> {
1214 let mut res = Vec::new();
1215 if let tlv::TlvItemValue::List(v) = inp {
1216 for item in v {
1217 res.push(ScheduleType {
1218 system_mode: item.get_int(&[0]).and_then(|v| SystemMode::from_u8(v as u8)),
1219 number_of_schedules: item.get_int(&[1]).map(|v| v as u8),
1220 schedule_type_features: item.get_int(&[2]).map(|v| v as u8),
1221 });
1222 }
1223 }
1224 Ok(res)
1225}
1226
1227pub fn decode_number_of_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1229 if let tlv::TlvItemValue::Int(v) = inp {
1230 Ok(*v as u8)
1231 } else {
1232 Err(anyhow::anyhow!("Expected UInt8"))
1233 }
1234}
1235
1236pub fn decode_number_of_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1238 if let tlv::TlvItemValue::Int(v) = inp {
1239 Ok(*v as u8)
1240 } else {
1241 Err(anyhow::anyhow!("Expected UInt8"))
1242 }
1243}
1244
1245pub fn decode_number_of_schedule_transitions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1247 if let tlv::TlvItemValue::Int(v) = inp {
1248 Ok(*v as u8)
1249 } else {
1250 Err(anyhow::anyhow!("Expected UInt8"))
1251 }
1252}
1253
1254pub fn decode_number_of_schedule_transition_per_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
1256 if let tlv::TlvItemValue::Int(v) = inp {
1257 Ok(Some(*v as u8))
1258 } else {
1259 Ok(None)
1260 }
1261}
1262
1263pub fn decode_active_preset_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1265 if let tlv::TlvItemValue::OctetString(v) = inp {
1266 Ok(Some(v.clone()))
1267 } else {
1268 Ok(None)
1269 }
1270}
1271
1272pub fn decode_active_schedule_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
1274 if let tlv::TlvItemValue::OctetString(v) = inp {
1275 Ok(Some(v.clone()))
1276 } else {
1277 Ok(None)
1278 }
1279}
1280
1281pub fn decode_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Preset>> {
1283 let mut res = Vec::new();
1284 if let tlv::TlvItemValue::List(v) = inp {
1285 for item in v {
1286 res.push(Preset {
1287 preset_handle: item.get_octet_string_owned(&[0]),
1288 preset_scenario: item.get_int(&[1]).and_then(|v| PresetScenario::from_u8(v as u8)),
1289 name: item.get_string_owned(&[2]),
1290 cooling_setpoint: item.get_int(&[3]).map(|v| v as i16),
1291 heating_setpoint: item.get_int(&[4]).map(|v| v as i16),
1292 built_in: item.get_bool(&[5]),
1293 });
1294 }
1295 }
1296 Ok(res)
1297}
1298
1299pub fn decode_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Schedule>> {
1301 let mut res = Vec::new();
1302 if let tlv::TlvItemValue::List(v) = inp {
1303 for item in v {
1304 res.push(Schedule {
1305 schedule_handle: item.get_octet_string_owned(&[0]),
1306 system_mode: item.get_int(&[1]).and_then(|v| SystemMode::from_u8(v as u8)),
1307 name: item.get_string_owned(&[2]),
1308 preset_handle: item.get_octet_string_owned(&[3]),
1309 transitions: {
1310 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
1311 let mut items = Vec::new();
1312 for list_item in l {
1313 items.push(ScheduleTransition {
1314 day_of_week: list_item.get_int(&[0]).map(|v| v as u8),
1315 transition_time: list_item.get_int(&[1]).map(|v| v as u16),
1316 preset_handle: list_item.get_octet_string_owned(&[2]),
1317 system_mode: list_item.get_int(&[3]).and_then(|v| SystemMode::from_u8(v as u8)),
1318 cooling_setpoint: list_item.get_int(&[4]).map(|v| v as i16),
1319 heating_setpoint: list_item.get_int(&[5]).map(|v| v as i16),
1320 });
1321 }
1322 Some(items)
1323 } else {
1324 None
1325 }
1326 },
1327 built_in: item.get_bool(&[5]),
1328 });
1329 }
1330 }
1331 Ok(res)
1332}
1333
1334pub fn decode_setpoint_hold_expiry_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
1336 if let tlv::TlvItemValue::Int(v) = inp {
1337 Ok(Some(*v))
1338 } else {
1339 Ok(None)
1340 }
1341}
1342
1343pub fn decode_max_thermostat_suggestions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
1345 if let tlv::TlvItemValue::Int(v) = inp {
1346 Ok(*v as u8)
1347 } else {
1348 Err(anyhow::anyhow!("Expected UInt8"))
1349 }
1350}
1351
1352pub fn decode_thermostat_suggestions(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ThermostatSuggestion>> {
1354 let mut res = Vec::new();
1355 if let tlv::TlvItemValue::List(v) = inp {
1356 for item in v {
1357 res.push(ThermostatSuggestion {
1358 unique_id: item.get_int(&[0]).map(|v| v as u8),
1359 preset_handle: item.get_octet_string_owned(&[1]),
1360 effective_time: item.get_int(&[2]),
1361 expiration_time: item.get_int(&[3]),
1362 });
1363 }
1364 }
1365 Ok(res)
1366}
1367
1368pub fn decode_current_thermostat_suggestion(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ThermostatSuggestion>> {
1370 if let tlv::TlvItemValue::List(_fields) = inp {
1371 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1373 Ok(Some(ThermostatSuggestion {
1374 unique_id: item.get_int(&[0]).map(|v| v as u8),
1375 preset_handle: item.get_octet_string_owned(&[1]),
1376 effective_time: item.get_int(&[2]),
1377 expiration_time: item.get_int(&[3]),
1378 }))
1379 } else {
1383 Ok(None)
1384 }
1386}
1387
1388pub fn decode_thermostat_suggestion_not_following_reason(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ThermostatSuggestionNotFollowingReason>> {
1390 if let tlv::TlvItemValue::Int(v) = inp {
1391 Ok(Some(*v as u8))
1392 } else {
1393 Ok(None)
1394 }
1395}
1396
1397
1398pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
1410 if cluster_id != 0x0201 {
1412 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0201, got {}\"}}", cluster_id);
1413 }
1414
1415 match attribute_id {
1416 0x0000 => {
1417 match decode_local_temperature(tlv_value) {
1418 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1419 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1420 }
1421 }
1422 0x0001 => {
1423 match decode_outdoor_temperature(tlv_value) {
1424 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1425 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1426 }
1427 }
1428 0x0002 => {
1429 match decode_occupancy(tlv_value) {
1430 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1431 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1432 }
1433 }
1434 0x0003 => {
1435 match decode_abs_min_heat_setpoint_limit(tlv_value) {
1436 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1437 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1438 }
1439 }
1440 0x0004 => {
1441 match decode_abs_max_heat_setpoint_limit(tlv_value) {
1442 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1443 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1444 }
1445 }
1446 0x0005 => {
1447 match decode_abs_min_cool_setpoint_limit(tlv_value) {
1448 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1449 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1450 }
1451 }
1452 0x0006 => {
1453 match decode_abs_max_cool_setpoint_limit(tlv_value) {
1454 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1455 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1456 }
1457 }
1458 0x0007 => {
1459 match decode_pi_cooling_demand(tlv_value) {
1460 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1461 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1462 }
1463 }
1464 0x0008 => {
1465 match decode_pi_heating_demand(tlv_value) {
1466 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1467 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1468 }
1469 }
1470 0x0009 => {
1471 match decode_hvac_system_type_configuration(tlv_value) {
1472 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1473 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1474 }
1475 }
1476 0x0010 => {
1477 match decode_local_temperature_calibration(tlv_value) {
1478 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1479 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1480 }
1481 }
1482 0x0011 => {
1483 match decode_occupied_cooling_setpoint(tlv_value) {
1484 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1485 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1486 }
1487 }
1488 0x0012 => {
1489 match decode_occupied_heating_setpoint(tlv_value) {
1490 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1491 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1492 }
1493 }
1494 0x0013 => {
1495 match decode_unoccupied_cooling_setpoint(tlv_value) {
1496 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1497 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1498 }
1499 }
1500 0x0014 => {
1501 match decode_unoccupied_heating_setpoint(tlv_value) {
1502 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1503 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1504 }
1505 }
1506 0x0015 => {
1507 match decode_min_heat_setpoint_limit(tlv_value) {
1508 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1509 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1510 }
1511 }
1512 0x0016 => {
1513 match decode_max_heat_setpoint_limit(tlv_value) {
1514 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1515 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1516 }
1517 }
1518 0x0017 => {
1519 match decode_min_cool_setpoint_limit(tlv_value) {
1520 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1521 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1522 }
1523 }
1524 0x0018 => {
1525 match decode_max_cool_setpoint_limit(tlv_value) {
1526 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1527 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1528 }
1529 }
1530 0x0019 => {
1531 match decode_min_setpoint_dead_band(tlv_value) {
1532 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1533 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1534 }
1535 }
1536 0x001A => {
1537 match decode_remote_sensing(tlv_value) {
1538 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1539 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1540 }
1541 }
1542 0x001B => {
1543 match decode_control_sequence_of_operation(tlv_value) {
1544 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1545 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1546 }
1547 }
1548 0x001C => {
1549 match decode_system_mode(tlv_value) {
1550 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1551 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1552 }
1553 }
1554 0x001E => {
1555 match decode_thermostat_running_mode(tlv_value) {
1556 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1557 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1558 }
1559 }
1560 0x0023 => {
1561 match decode_temperature_setpoint_hold(tlv_value) {
1562 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1563 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1564 }
1565 }
1566 0x0024 => {
1567 match decode_temperature_setpoint_hold_duration(tlv_value) {
1568 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1569 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1570 }
1571 }
1572 0x0025 => {
1573 match decode_thermostat_programming_operation_mode(tlv_value) {
1574 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1575 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1576 }
1577 }
1578 0x0029 => {
1579 match decode_thermostat_running_state(tlv_value) {
1580 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1581 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1582 }
1583 }
1584 0x0030 => {
1585 match decode_setpoint_change_source(tlv_value) {
1586 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1587 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1588 }
1589 }
1590 0x0031 => {
1591 match decode_setpoint_change_amount(tlv_value) {
1592 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1593 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1594 }
1595 }
1596 0x0032 => {
1597 match decode_setpoint_change_source_timestamp(tlv_value) {
1598 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1599 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1600 }
1601 }
1602 0x0034 => {
1603 match decode_occupied_setback(tlv_value) {
1604 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1605 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1606 }
1607 }
1608 0x0035 => {
1609 match decode_occupied_setback_min(tlv_value) {
1610 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1611 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1612 }
1613 }
1614 0x0036 => {
1615 match decode_occupied_setback_max(tlv_value) {
1616 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1617 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1618 }
1619 }
1620 0x0037 => {
1621 match decode_unoccupied_setback(tlv_value) {
1622 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1623 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1624 }
1625 }
1626 0x0038 => {
1627 match decode_unoccupied_setback_min(tlv_value) {
1628 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1629 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1630 }
1631 }
1632 0x0039 => {
1633 match decode_unoccupied_setback_max(tlv_value) {
1634 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1635 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1636 }
1637 }
1638 0x003A => {
1639 match decode_emergency_heat_delta(tlv_value) {
1640 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1641 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1642 }
1643 }
1644 0x0040 => {
1645 match decode_ac_type(tlv_value) {
1646 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1647 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1648 }
1649 }
1650 0x0041 => {
1651 match decode_ac_capacity(tlv_value) {
1652 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1653 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1654 }
1655 }
1656 0x0042 => {
1657 match decode_ac_refrigerant_type(tlv_value) {
1658 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1659 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1660 }
1661 }
1662 0x0043 => {
1663 match decode_ac_compressor_type(tlv_value) {
1664 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1665 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1666 }
1667 }
1668 0x0044 => {
1669 match decode_ac_error_code(tlv_value) {
1670 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1671 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1672 }
1673 }
1674 0x0045 => {
1675 match decode_aclouver_position(tlv_value) {
1676 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1677 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1678 }
1679 }
1680 0x0046 => {
1681 match decode_ac_coil_temperature(tlv_value) {
1682 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1683 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1684 }
1685 }
1686 0x0047 => {
1687 match decode_ac_capacity_format(tlv_value) {
1688 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1689 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1690 }
1691 }
1692 0x0048 => {
1693 match decode_preset_types(tlv_value) {
1694 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1695 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1696 }
1697 }
1698 0x0049 => {
1699 match decode_schedule_types(tlv_value) {
1700 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1701 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1702 }
1703 }
1704 0x004A => {
1705 match decode_number_of_presets(tlv_value) {
1706 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1707 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1708 }
1709 }
1710 0x004B => {
1711 match decode_number_of_schedules(tlv_value) {
1712 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1713 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1714 }
1715 }
1716 0x004C => {
1717 match decode_number_of_schedule_transitions(tlv_value) {
1718 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1719 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1720 }
1721 }
1722 0x004D => {
1723 match decode_number_of_schedule_transition_per_day(tlv_value) {
1724 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1725 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1726 }
1727 }
1728 0x004E => {
1729 match decode_active_preset_handle(tlv_value) {
1730 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1731 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1732 }
1733 }
1734 0x004F => {
1735 match decode_active_schedule_handle(tlv_value) {
1736 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1737 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1738 }
1739 }
1740 0x0050 => {
1741 match decode_presets(tlv_value) {
1742 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1743 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1744 }
1745 }
1746 0x0051 => {
1747 match decode_schedules(tlv_value) {
1748 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1749 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1750 }
1751 }
1752 0x0052 => {
1753 match decode_setpoint_hold_expiry_timestamp(tlv_value) {
1754 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1755 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1756 }
1757 }
1758 0x0053 => {
1759 match decode_max_thermostat_suggestions(tlv_value) {
1760 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1761 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1762 }
1763 }
1764 0x0054 => {
1765 match decode_thermostat_suggestions(tlv_value) {
1766 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1767 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1768 }
1769 }
1770 0x0055 => {
1771 match decode_current_thermostat_suggestion(tlv_value) {
1772 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1773 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1774 }
1775 }
1776 0x0056 => {
1777 match decode_thermostat_suggestion_not_following_reason(tlv_value) {
1778 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
1779 Err(e) => format!("{{\"error\": \"{}\"}}", e),
1780 }
1781 }
1782 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
1783 }
1784}
1785
1786pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
1791 vec![
1792 (0x0000, "LocalTemperature"),
1793 (0x0001, "OutdoorTemperature"),
1794 (0x0002, "Occupancy"),
1795 (0x0003, "AbsMinHeatSetpointLimit"),
1796 (0x0004, "AbsMaxHeatSetpointLimit"),
1797 (0x0005, "AbsMinCoolSetpointLimit"),
1798 (0x0006, "AbsMaxCoolSetpointLimit"),
1799 (0x0007, "PICoolingDemand"),
1800 (0x0008, "PIHeatingDemand"),
1801 (0x0009, "HVACSystemTypeConfiguration"),
1802 (0x0010, "LocalTemperatureCalibration"),
1803 (0x0011, "OccupiedCoolingSetpoint"),
1804 (0x0012, "OccupiedHeatingSetpoint"),
1805 (0x0013, "UnoccupiedCoolingSetpoint"),
1806 (0x0014, "UnoccupiedHeatingSetpoint"),
1807 (0x0015, "MinHeatSetpointLimit"),
1808 (0x0016, "MaxHeatSetpointLimit"),
1809 (0x0017, "MinCoolSetpointLimit"),
1810 (0x0018, "MaxCoolSetpointLimit"),
1811 (0x0019, "MinSetpointDeadBand"),
1812 (0x001A, "RemoteSensing"),
1813 (0x001B, "ControlSequenceOfOperation"),
1814 (0x001C, "SystemMode"),
1815 (0x001E, "ThermostatRunningMode"),
1816 (0x0023, "TemperatureSetpointHold"),
1817 (0x0024, "TemperatureSetpointHoldDuration"),
1818 (0x0025, "ThermostatProgrammingOperationMode"),
1819 (0x0029, "ThermostatRunningState"),
1820 (0x0030, "SetpointChangeSource"),
1821 (0x0031, "SetpointChangeAmount"),
1822 (0x0032, "SetpointChangeSourceTimestamp"),
1823 (0x0034, "OccupiedSetback"),
1824 (0x0035, "OccupiedSetbackMin"),
1825 (0x0036, "OccupiedSetbackMax"),
1826 (0x0037, "UnoccupiedSetback"),
1827 (0x0038, "UnoccupiedSetbackMin"),
1828 (0x0039, "UnoccupiedSetbackMax"),
1829 (0x003A, "EmergencyHeatDelta"),
1830 (0x0040, "ACType"),
1831 (0x0041, "ACCapacity"),
1832 (0x0042, "ACRefrigerantType"),
1833 (0x0043, "ACCompressorType"),
1834 (0x0044, "ACErrorCode"),
1835 (0x0045, "ACLouverPosition"),
1836 (0x0046, "ACCoilTemperature"),
1837 (0x0047, "ACCapacityFormat"),
1838 (0x0048, "PresetTypes"),
1839 (0x0049, "ScheduleTypes"),
1840 (0x004A, "NumberOfPresets"),
1841 (0x004B, "NumberOfSchedules"),
1842 (0x004C, "NumberOfScheduleTransitions"),
1843 (0x004D, "NumberOfScheduleTransitionPerDay"),
1844 (0x004E, "ActivePresetHandle"),
1845 (0x004F, "ActiveScheduleHandle"),
1846 (0x0050, "Presets"),
1847 (0x0051, "Schedules"),
1848 (0x0052, "SetpointHoldExpiryTimestamp"),
1849 (0x0053, "MaxThermostatSuggestions"),
1850 (0x0054, "ThermostatSuggestions"),
1851 (0x0055, "CurrentThermostatSuggestion"),
1852 (0x0056, "ThermostatSuggestionNotFollowingReason"),
1853 ]
1854}
1855
1856pub fn get_command_list() -> Vec<(u32, &'static str)> {
1859 vec![
1860 (0x00, "SetpointRaiseLower"),
1861 (0x05, "SetActiveScheduleRequest"),
1862 (0x06, "SetActivePresetRequest"),
1863 (0x07, "AddThermostatSuggestion"),
1864 (0x08, "RemoveThermostatSuggestion"),
1865 ]
1866}
1867
1868pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
1869 match cmd_id {
1870 0x00 => Some("SetpointRaiseLower"),
1871 0x05 => Some("SetActiveScheduleRequest"),
1872 0x06 => Some("SetActivePresetRequest"),
1873 0x07 => Some("AddThermostatSuggestion"),
1874 0x08 => Some("RemoveThermostatSuggestion"),
1875 _ => None,
1876 }
1877}
1878
1879pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
1880 match cmd_id {
1881 0x00 => Some(vec![
1882 crate::clusters::codec::CommandField { tag: 0, name: "mode", kind: crate::clusters::codec::FieldKind::Enum { name: "SetpointRaiseLowerMode", variants: &[(0, "Heat"), (1, "Cool"), (2, "Both")] }, optional: false, nullable: false },
1883 crate::clusters::codec::CommandField { tag: 1, name: "amount", kind: crate::clusters::codec::FieldKind::I8, optional: false, nullable: false },
1884 ]),
1885 0x05 => Some(vec![
1886 crate::clusters::codec::CommandField { tag: 0, name: "schedule_handle", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
1887 ]),
1888 0x06 => Some(vec![
1889 crate::clusters::codec::CommandField { tag: 0, name: "preset_handle", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: true },
1890 ]),
1891 0x07 => Some(vec![
1892 crate::clusters::codec::CommandField { tag: 0, name: "preset_handle", kind: crate::clusters::codec::FieldKind::OctetString, optional: false, nullable: false },
1893 crate::clusters::codec::CommandField { tag: 1, name: "effective_time", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: true },
1894 crate::clusters::codec::CommandField { tag: 2, name: "expiration_in_minutes", kind: crate::clusters::codec::FieldKind::U16, optional: false, nullable: false },
1895 ]),
1896 0x08 => Some(vec![
1897 crate::clusters::codec::CommandField { tag: 0, name: "unique_id", kind: crate::clusters::codec::FieldKind::U8, optional: false, nullable: false },
1898 ]),
1899 _ => None,
1900 }
1901}
1902
1903pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
1904 match cmd_id {
1905 0x00 => {
1906 let mode = {
1907 let n = crate::clusters::codec::json_util::get_u64(args, "mode")?;
1908 SetpointRaiseLowerMode::from_u8(n as u8).ok_or_else(|| anyhow::anyhow!("invalid SetpointRaiseLowerMode: {}", n))?
1909 };
1910 let amount = crate::clusters::codec::json_util::get_i8(args, "amount")?;
1911 encode_setpoint_raise_lower(mode, amount)
1912 }
1913 0x05 => {
1914 let schedule_handle = crate::clusters::codec::json_util::get_octstr(args, "schedule_handle")?;
1915 encode_set_active_schedule_request(schedule_handle)
1916 }
1917 0x06 => {
1918 let preset_handle = crate::clusters::codec::json_util::get_opt_octstr(args, "preset_handle")?;
1919 encode_set_active_preset_request(preset_handle)
1920 }
1921 0x07 => {
1922 let preset_handle = crate::clusters::codec::json_util::get_octstr(args, "preset_handle")?;
1923 let effective_time = crate::clusters::codec::json_util::get_opt_u64(args, "effective_time")?;
1924 let expiration_in_minutes = crate::clusters::codec::json_util::get_u16(args, "expiration_in_minutes")?;
1925 encode_add_thermostat_suggestion(preset_handle, effective_time, expiration_in_minutes)
1926 }
1927 0x08 => {
1928 let unique_id = crate::clusters::codec::json_util::get_u8(args, "unique_id")?;
1929 encode_remove_thermostat_suggestion(unique_id)
1930 }
1931 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
1932 }
1933}
1934
1935#[derive(Debug, serde::Serialize)]
1936pub struct AddThermostatSuggestionResponse {
1937 pub unique_id: Option<u8>,
1938}
1939
1940pub fn decode_add_thermostat_suggestion_response(inp: &tlv::TlvItemValue) -> anyhow::Result<AddThermostatSuggestionResponse> {
1944 if let tlv::TlvItemValue::List(_fields) = inp {
1945 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1946 Ok(AddThermostatSuggestionResponse {
1947 unique_id: item.get_int(&[0]).map(|v| v as u8),
1948 })
1949 } else {
1950 Err(anyhow::anyhow!("Expected struct fields"))
1951 }
1952}
1953
1954pub async fn setpoint_raise_lower(conn: &crate::controller::Connection, endpoint: u16, mode: SetpointRaiseLowerMode, amount: i8) -> anyhow::Result<()> {
1958 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_CMD_ID_SETPOINTRAISELOWER, &encode_setpoint_raise_lower(mode, amount)?).await?;
1959 Ok(())
1960}
1961
1962pub async fn set_active_schedule_request(conn: &crate::controller::Connection, endpoint: u16, schedule_handle: Vec<u8>) -> anyhow::Result<()> {
1964 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_CMD_ID_SETACTIVESCHEDULEREQUEST, &encode_set_active_schedule_request(schedule_handle)?).await?;
1965 Ok(())
1966}
1967
1968pub async fn set_active_preset_request(conn: &crate::controller::Connection, endpoint: u16, preset_handle: Option<Vec<u8>>) -> anyhow::Result<()> {
1970 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_CMD_ID_SETACTIVEPRESETREQUEST, &encode_set_active_preset_request(preset_handle)?).await?;
1971 Ok(())
1972}
1973
1974pub async fn add_thermostat_suggestion(conn: &crate::controller::Connection, endpoint: u16, preset_handle: Vec<u8>, effective_time: Option<u64>, expiration_in_minutes: u16) -> anyhow::Result<AddThermostatSuggestionResponse> {
1976 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_CMD_ID_ADDTHERMOSTATSUGGESTION, &encode_add_thermostat_suggestion(preset_handle, effective_time, expiration_in_minutes)?).await?;
1977 decode_add_thermostat_suggestion_response(&tlv)
1978}
1979
1980pub async fn remove_thermostat_suggestion(conn: &crate::controller::Connection, endpoint: u16, unique_id: u8) -> anyhow::Result<()> {
1982 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_CMD_ID_REMOVETHERMOSTATSUGGESTION, &encode_remove_thermostat_suggestion(unique_id)?).await?;
1983 Ok(())
1984}
1985
1986pub async fn read_local_temperature(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i16>> {
1988 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_LOCALTEMPERATURE).await?;
1989 decode_local_temperature(&tlv)
1990}
1991
1992pub async fn read_outdoor_temperature(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i16>> {
1994 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OUTDOORTEMPERATURE).await?;
1995 decode_outdoor_temperature(&tlv)
1996}
1997
1998pub async fn read_occupancy(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Occupancy> {
2000 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OCCUPANCY).await?;
2001 decode_occupancy(&tlv)
2002}
2003
2004pub async fn read_abs_min_heat_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2006 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ABSMINHEATSETPOINTLIMIT).await?;
2007 decode_abs_min_heat_setpoint_limit(&tlv)
2008}
2009
2010pub async fn read_abs_max_heat_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2012 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ABSMAXHEATSETPOINTLIMIT).await?;
2013 decode_abs_max_heat_setpoint_limit(&tlv)
2014}
2015
2016pub async fn read_abs_min_cool_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2018 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ABSMINCOOLSETPOINTLIMIT).await?;
2019 decode_abs_min_cool_setpoint_limit(&tlv)
2020}
2021
2022pub async fn read_abs_max_cool_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2024 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ABSMAXCOOLSETPOINTLIMIT).await?;
2025 decode_abs_max_cool_setpoint_limit(&tlv)
2026}
2027
2028pub async fn read_pi_cooling_demand(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2030 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_PICOOLINGDEMAND).await?;
2031 decode_pi_cooling_demand(&tlv)
2032}
2033
2034pub async fn read_pi_heating_demand(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2036 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_PIHEATINGDEMAND).await?;
2037 decode_pi_heating_demand(&tlv)
2038}
2039
2040pub async fn read_hvac_system_type_configuration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2042 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_HVACSYSTEMTYPECONFIGURATION).await?;
2043 decode_hvac_system_type_configuration(&tlv)
2044}
2045
2046pub async fn read_local_temperature_calibration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i8> {
2048 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_LOCALTEMPERATURECALIBRATION).await?;
2049 decode_local_temperature_calibration(&tlv)
2050}
2051
2052pub async fn read_occupied_cooling_setpoint(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2054 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OCCUPIEDCOOLINGSETPOINT).await?;
2055 decode_occupied_cooling_setpoint(&tlv)
2056}
2057
2058pub async fn read_occupied_heating_setpoint(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2060 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OCCUPIEDHEATINGSETPOINT).await?;
2061 decode_occupied_heating_setpoint(&tlv)
2062}
2063
2064pub async fn read_unoccupied_cooling_setpoint(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2066 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_UNOCCUPIEDCOOLINGSETPOINT).await?;
2067 decode_unoccupied_cooling_setpoint(&tlv)
2068}
2069
2070pub async fn read_unoccupied_heating_setpoint(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2072 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_UNOCCUPIEDHEATINGSETPOINT).await?;
2073 decode_unoccupied_heating_setpoint(&tlv)
2074}
2075
2076pub async fn read_min_heat_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2078 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_MINHEATSETPOINTLIMIT).await?;
2079 decode_min_heat_setpoint_limit(&tlv)
2080}
2081
2082pub async fn read_max_heat_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2084 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_MAXHEATSETPOINTLIMIT).await?;
2085 decode_max_heat_setpoint_limit(&tlv)
2086}
2087
2088pub async fn read_min_cool_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2090 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_MINCOOLSETPOINTLIMIT).await?;
2091 decode_min_cool_setpoint_limit(&tlv)
2092}
2093
2094pub async fn read_max_cool_setpoint_limit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i16> {
2096 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_MAXCOOLSETPOINTLIMIT).await?;
2097 decode_max_cool_setpoint_limit(&tlv)
2098}
2099
2100pub async fn read_min_setpoint_dead_band(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<i8> {
2102 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_MINSETPOINTDEADBAND).await?;
2103 decode_min_setpoint_dead_band(&tlv)
2104}
2105
2106pub async fn read_remote_sensing(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RemoteSensing> {
2108 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_REMOTESENSING).await?;
2109 decode_remote_sensing(&tlv)
2110}
2111
2112pub async fn read_control_sequence_of_operation(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ControlSequenceOfOperation> {
2114 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_CONTROLSEQUENCEOFOPERATION).await?;
2115 decode_control_sequence_of_operation(&tlv)
2116}
2117
2118pub async fn read_system_mode(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<SystemMode> {
2120 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SYSTEMMODE).await?;
2121 decode_system_mode(&tlv)
2122}
2123
2124pub async fn read_thermostat_running_mode(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ThermostatRunningMode> {
2126 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_THERMOSTATRUNNINGMODE).await?;
2127 decode_thermostat_running_mode(&tlv)
2128}
2129
2130pub async fn read_temperature_setpoint_hold(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<TemperatureSetpointHold> {
2132 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_TEMPERATURESETPOINTHOLD).await?;
2133 decode_temperature_setpoint_hold(&tlv)
2134}
2135
2136pub async fn read_temperature_setpoint_hold_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u16>> {
2138 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_TEMPERATURESETPOINTHOLDDURATION).await?;
2139 decode_temperature_setpoint_hold_duration(&tlv)
2140}
2141
2142pub async fn read_thermostat_programming_operation_mode(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2144 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_THERMOSTATPROGRAMMINGOPERATIONMODE).await?;
2145 decode_thermostat_programming_operation_mode(&tlv)
2146}
2147
2148pub async fn read_thermostat_running_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<RelayState> {
2150 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_THERMOSTATRUNNINGSTATE).await?;
2151 decode_thermostat_running_state(&tlv)
2152}
2153
2154pub async fn read_setpoint_change_source(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<SetpointChangeSource> {
2156 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SETPOINTCHANGESOURCE).await?;
2157 decode_setpoint_change_source(&tlv)
2158}
2159
2160pub async fn read_setpoint_change_amount(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
2162 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SETPOINTCHANGEAMOUNT).await?;
2163 decode_setpoint_change_amount(&tlv)
2164}
2165
2166pub async fn read_setpoint_change_source_timestamp(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u64> {
2168 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SETPOINTCHANGESOURCETIMESTAMP).await?;
2169 decode_setpoint_change_source_timestamp(&tlv)
2170}
2171
2172pub async fn read_occupied_setback(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2174 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OCCUPIEDSETBACK).await?;
2175 decode_occupied_setback(&tlv)
2176}
2177
2178pub async fn read_occupied_setback_min(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2180 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OCCUPIEDSETBACKMIN).await?;
2181 decode_occupied_setback_min(&tlv)
2182}
2183
2184pub async fn read_occupied_setback_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2186 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_OCCUPIEDSETBACKMAX).await?;
2187 decode_occupied_setback_max(&tlv)
2188}
2189
2190pub async fn read_unoccupied_setback(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2192 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_UNOCCUPIEDSETBACK).await?;
2193 decode_unoccupied_setback(&tlv)
2194}
2195
2196pub async fn read_unoccupied_setback_min(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2198 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_UNOCCUPIEDSETBACKMIN).await?;
2199 decode_unoccupied_setback_min(&tlv)
2200}
2201
2202pub async fn read_unoccupied_setback_max(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2204 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_UNOCCUPIEDSETBACKMAX).await?;
2205 decode_unoccupied_setback_max(&tlv)
2206}
2207
2208pub async fn read_emergency_heat_delta(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2210 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_EMERGENCYHEATDELTA).await?;
2211 decode_emergency_heat_delta(&tlv)
2212}
2213
2214pub async fn read_ac_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ACType> {
2216 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACTYPE).await?;
2217 decode_ac_type(&tlv)
2218}
2219
2220pub async fn read_ac_capacity(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u16> {
2222 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACCAPACITY).await?;
2223 decode_ac_capacity(&tlv)
2224}
2225
2226pub async fn read_ac_refrigerant_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ACRefrigerantType> {
2228 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACREFRIGERANTTYPE).await?;
2229 decode_ac_refrigerant_type(&tlv)
2230}
2231
2232pub async fn read_ac_compressor_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ACCompressorType> {
2234 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACCOMPRESSORTYPE).await?;
2235 decode_ac_compressor_type(&tlv)
2236}
2237
2238pub async fn read_ac_error_code(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ACErrorCode> {
2240 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACERRORCODE).await?;
2241 decode_ac_error_code(&tlv)
2242}
2243
2244pub async fn read_aclouver_position(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ACLouverPosition> {
2246 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACLOUVERPOSITION).await?;
2247 decode_aclouver_position(&tlv)
2248}
2249
2250pub async fn read_ac_coil_temperature(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i16>> {
2252 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACCOILTEMPERATURE).await?;
2253 decode_ac_coil_temperature(&tlv)
2254}
2255
2256pub async fn read_ac_capacity_format(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<ACCapacityFormat> {
2258 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACCAPACITYFORMAT).await?;
2259 decode_ac_capacity_format(&tlv)
2260}
2261
2262pub async fn read_preset_types(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<PresetType>> {
2264 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_PRESETTYPES).await?;
2265 decode_preset_types(&tlv)
2266}
2267
2268pub async fn read_schedule_types(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ScheduleType>> {
2270 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SCHEDULETYPES).await?;
2271 decode_schedule_types(&tlv)
2272}
2273
2274pub async fn read_number_of_presets(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2276 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_NUMBEROFPRESETS).await?;
2277 decode_number_of_presets(&tlv)
2278}
2279
2280pub async fn read_number_of_schedules(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2282 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_NUMBEROFSCHEDULES).await?;
2283 decode_number_of_schedules(&tlv)
2284}
2285
2286pub async fn read_number_of_schedule_transitions(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2288 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_NUMBEROFSCHEDULETRANSITIONS).await?;
2289 decode_number_of_schedule_transitions(&tlv)
2290}
2291
2292pub async fn read_number_of_schedule_transition_per_day(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
2294 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_NUMBEROFSCHEDULETRANSITIONPERDAY).await?;
2295 decode_number_of_schedule_transition_per_day(&tlv)
2296}
2297
2298pub async fn read_active_preset_handle(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Vec<u8>>> {
2300 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACTIVEPRESETHANDLE).await?;
2301 decode_active_preset_handle(&tlv)
2302}
2303
2304pub async fn read_active_schedule_handle(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Vec<u8>>> {
2306 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_ACTIVESCHEDULEHANDLE).await?;
2307 decode_active_schedule_handle(&tlv)
2308}
2309
2310pub async fn read_presets(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Preset>> {
2312 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_PRESETS).await?;
2313 decode_presets(&tlv)
2314}
2315
2316pub async fn read_schedules(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Schedule>> {
2318 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SCHEDULES).await?;
2319 decode_schedules(&tlv)
2320}
2321
2322pub async fn read_setpoint_hold_expiry_timestamp(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
2324 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_SETPOINTHOLDEXPIRYTIMESTAMP).await?;
2325 decode_setpoint_hold_expiry_timestamp(&tlv)
2326}
2327
2328pub async fn read_max_thermostat_suggestions(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
2330 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_MAXTHERMOSTATSUGGESTIONS).await?;
2331 decode_max_thermostat_suggestions(&tlv)
2332}
2333
2334pub async fn read_thermostat_suggestions(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ThermostatSuggestion>> {
2336 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_THERMOSTATSUGGESTIONS).await?;
2337 decode_thermostat_suggestions(&tlv)
2338}
2339
2340pub async fn read_current_thermostat_suggestion(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<ThermostatSuggestion>> {
2342 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_CURRENTTHERMOSTATSUGGESTION).await?;
2343 decode_current_thermostat_suggestion(&tlv)
2344}
2345
2346pub async fn read_thermostat_suggestion_not_following_reason(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<ThermostatSuggestionNotFollowingReason>> {
2348 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_THERMOSTAT, crate::clusters::defs::CLUSTER_THERMOSTAT_ATTR_ID_THERMOSTATSUGGESTIONNOTFOLLOWINGREASON).await?;
2349 decode_thermostat_suggestion_not_following_reason(&tlv)
2350}
2351
2352#[derive(Debug, serde::Serialize)]
2353pub struct SystemModeChangeEvent {
2354 pub previous_system_mode: Option<SystemMode>,
2355 pub current_system_mode: Option<SystemMode>,
2356}
2357
2358#[derive(Debug, serde::Serialize)]
2359pub struct LocalTemperatureChangeEvent {
2360 pub current_local_temperature: Option<i16>,
2361}
2362
2363#[derive(Debug, serde::Serialize)]
2364pub struct OccupancyChangeEvent {
2365 pub previous_occupancy: Option<Occupancy>,
2366 pub current_occupancy: Option<Occupancy>,
2367}
2368
2369#[derive(Debug, serde::Serialize)]
2370pub struct SetpointChangeEvent {
2371 pub system_mode: Option<SystemMode>,
2372 pub occupancy: Option<Occupancy>,
2373 pub previous_setpoint: Option<i16>,
2374 pub current_setpoint: Option<i16>,
2375}
2376
2377#[derive(Debug, serde::Serialize)]
2378pub struct RunningStateChangeEvent {
2379 pub previous_running_state: Option<RelayState>,
2380 pub current_running_state: Option<RelayState>,
2381}
2382
2383#[derive(Debug, serde::Serialize)]
2384pub struct RunningModeChangeEvent {
2385 pub previous_running_mode: Option<ThermostatRunningMode>,
2386 pub current_running_mode: Option<ThermostatRunningMode>,
2387}
2388
2389#[derive(Debug, serde::Serialize)]
2390pub struct ActiveScheduleChangeEvent {
2391 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
2392 pub previous_schedule_handle: Option<Vec<u8>>,
2393 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
2394 pub current_schedule_handle: Option<Vec<u8>>,
2395}
2396
2397#[derive(Debug, serde::Serialize)]
2398pub struct ActivePresetChangeEvent {
2399 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
2400 pub previous_preset_handle: Option<Vec<u8>>,
2401 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
2402 pub current_preset_handle: Option<Vec<u8>>,
2403}
2404
2405pub fn decode_system_mode_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<SystemModeChangeEvent> {
2409 if let tlv::TlvItemValue::List(_fields) = inp {
2410 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2411 Ok(SystemModeChangeEvent {
2412 previous_system_mode: item.get_int(&[0]).and_then(|v| SystemMode::from_u8(v as u8)),
2413 current_system_mode: item.get_int(&[1]).and_then(|v| SystemMode::from_u8(v as u8)),
2414 })
2415 } else {
2416 Err(anyhow::anyhow!("Expected struct fields"))
2417 }
2418}
2419
2420pub fn decode_local_temperature_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<LocalTemperatureChangeEvent> {
2422 if let tlv::TlvItemValue::List(_fields) = inp {
2423 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2424 Ok(LocalTemperatureChangeEvent {
2425 current_local_temperature: item.get_int(&[0]).map(|v| v as i16),
2426 })
2427 } else {
2428 Err(anyhow::anyhow!("Expected struct fields"))
2429 }
2430}
2431
2432pub fn decode_occupancy_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<OccupancyChangeEvent> {
2434 if let tlv::TlvItemValue::List(_fields) = inp {
2435 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2436 Ok(OccupancyChangeEvent {
2437 previous_occupancy: item.get_int(&[0]).map(|v| v as u8),
2438 current_occupancy: item.get_int(&[1]).map(|v| v as u8),
2439 })
2440 } else {
2441 Err(anyhow::anyhow!("Expected struct fields"))
2442 }
2443}
2444
2445pub fn decode_setpoint_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<SetpointChangeEvent> {
2447 if let tlv::TlvItemValue::List(_fields) = inp {
2448 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2449 Ok(SetpointChangeEvent {
2450 system_mode: item.get_int(&[0]).and_then(|v| SystemMode::from_u8(v as u8)),
2451 occupancy: item.get_int(&[1]).map(|v| v as u8),
2452 previous_setpoint: item.get_int(&[2]).map(|v| v as i16),
2453 current_setpoint: item.get_int(&[3]).map(|v| v as i16),
2454 })
2455 } else {
2456 Err(anyhow::anyhow!("Expected struct fields"))
2457 }
2458}
2459
2460pub fn decode_running_state_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RunningStateChangeEvent> {
2462 if let tlv::TlvItemValue::List(_fields) = inp {
2463 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2464 Ok(RunningStateChangeEvent {
2465 previous_running_state: item.get_int(&[0]).map(|v| v as u8),
2466 current_running_state: item.get_int(&[1]).map(|v| v as u8),
2467 })
2468 } else {
2469 Err(anyhow::anyhow!("Expected struct fields"))
2470 }
2471}
2472
2473pub fn decode_running_mode_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RunningModeChangeEvent> {
2475 if let tlv::TlvItemValue::List(_fields) = inp {
2476 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2477 Ok(RunningModeChangeEvent {
2478 previous_running_mode: item.get_int(&[0]).and_then(|v| ThermostatRunningMode::from_u8(v as u8)),
2479 current_running_mode: item.get_int(&[1]).and_then(|v| ThermostatRunningMode::from_u8(v as u8)),
2480 })
2481 } else {
2482 Err(anyhow::anyhow!("Expected struct fields"))
2483 }
2484}
2485
2486pub fn decode_active_schedule_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ActiveScheduleChangeEvent> {
2488 if let tlv::TlvItemValue::List(_fields) = inp {
2489 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2490 Ok(ActiveScheduleChangeEvent {
2491 previous_schedule_handle: item.get_octet_string_owned(&[0]),
2492 current_schedule_handle: item.get_octet_string_owned(&[1]),
2493 })
2494 } else {
2495 Err(anyhow::anyhow!("Expected struct fields"))
2496 }
2497}
2498
2499pub fn decode_active_preset_change_event(inp: &tlv::TlvItemValue) -> anyhow::Result<ActivePresetChangeEvent> {
2501 if let tlv::TlvItemValue::List(_fields) = inp {
2502 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
2503 Ok(ActivePresetChangeEvent {
2504 previous_preset_handle: item.get_octet_string_owned(&[0]),
2505 current_preset_handle: item.get_octet_string_owned(&[1]),
2506 })
2507 } else {
2508 Err(anyhow::anyhow!("Expected struct fields"))
2509 }
2510}
2511
2512
2513pub fn decode_event_json(cluster_id: u32, event_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
2517 if cluster_id != 0x0201 {
2518 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0201, got {}\"}}", cluster_id);
2519 }
2520
2521 match event_id {
2522 0x00 => {
2523 match decode_system_mode_change_event(tlv_value) {
2524 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2525 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2526 }
2527 }
2528 0x01 => {
2529 match decode_local_temperature_change_event(tlv_value) {
2530 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2531 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2532 }
2533 }
2534 0x02 => {
2535 match decode_occupancy_change_event(tlv_value) {
2536 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2537 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2538 }
2539 }
2540 0x03 => {
2541 match decode_setpoint_change_event(tlv_value) {
2542 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2543 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2544 }
2545 }
2546 0x04 => {
2547 match decode_running_state_change_event(tlv_value) {
2548 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2549 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2550 }
2551 }
2552 0x05 => {
2553 match decode_running_mode_change_event(tlv_value) {
2554 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2555 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2556 }
2557 }
2558 0x06 => {
2559 match decode_active_schedule_change_event(tlv_value) {
2560 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2561 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2562 }
2563 }
2564 0x07 => {
2565 match decode_active_preset_change_event(tlv_value) {
2566 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
2567 Err(e) => format!("{{\"error\": \"{}\"}}", e),
2568 }
2569 }
2570 _ => format!("{{\"error\": \"Unknown event ID: {}\"}}", event_id),
2571 }
2572}
2573
2574pub fn get_event_list() -> Vec<(u32, &'static str)> {
2579 vec![
2580 (0x00, "SystemModeChange"),
2581 (0x01, "LocalTemperatureChange"),
2582 (0x02, "OccupancyChange"),
2583 (0x03, "SetpointChange"),
2584 (0x04, "RunningStateChange"),
2585 (0x05, "RunningModeChange"),
2586 (0x06, "ActiveScheduleChange"),
2587 (0x07, "ActivePresetChange"),
2588 ]
2589}
2590