use crate::tlv;
use anyhow;
use serde_json;
use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ACCapacityFormat {
Btuh = 0,
}
impl ACCapacityFormat {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ACCapacityFormat::Btuh),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ACCapacityFormat> for u8 {
fn from(val: ACCapacityFormat) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ACCompressorType {
Unknown = 0,
T1 = 1,
T2 = 2,
T3 = 3,
}
impl ACCompressorType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ACCompressorType::Unknown),
1 => Some(ACCompressorType::T1),
2 => Some(ACCompressorType::T2),
3 => Some(ACCompressorType::T3),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ACCompressorType> for u8 {
fn from(val: ACCompressorType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ACLouverPosition {
Closed = 1,
Open = 2,
Quarter = 3,
Half = 4,
Threequarters = 5,
}
impl ACLouverPosition {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(ACLouverPosition::Closed),
2 => Some(ACLouverPosition::Open),
3 => Some(ACLouverPosition::Quarter),
4 => Some(ACLouverPosition::Half),
5 => Some(ACLouverPosition::Threequarters),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ACLouverPosition> for u8 {
fn from(val: ACLouverPosition) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ACRefrigerantType {
Unknown = 0,
R22 = 1,
R410a = 2,
R407c = 3,
}
impl ACRefrigerantType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ACRefrigerantType::Unknown),
1 => Some(ACRefrigerantType::R22),
2 => Some(ACRefrigerantType::R410a),
3 => Some(ACRefrigerantType::R407c),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ACRefrigerantType> for u8 {
fn from(val: ACRefrigerantType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ACType {
Unknown = 0,
Coolingfixed = 1,
Heatpumpfixed = 2,
Coolinginverter = 3,
Heatpumpinverter = 4,
}
impl ACType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ACType::Unknown),
1 => Some(ACType::Coolingfixed),
2 => Some(ACType::Heatpumpfixed),
3 => Some(ACType::Coolinginverter),
4 => Some(ACType::Heatpumpinverter),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ACType> for u8 {
fn from(val: ACType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ControlSequenceOfOperation {
Coolingonly = 0,
Coolingwithreheat = 1,
Heatingonly = 2,
Heatingwithreheat = 3,
Coolingandheating = 4,
Coolingandheatingwithreheat = 5,
}
impl ControlSequenceOfOperation {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ControlSequenceOfOperation::Coolingonly),
1 => Some(ControlSequenceOfOperation::Coolingwithreheat),
2 => Some(ControlSequenceOfOperation::Heatingonly),
3 => Some(ControlSequenceOfOperation::Heatingwithreheat),
4 => Some(ControlSequenceOfOperation::Coolingandheating),
5 => Some(ControlSequenceOfOperation::Coolingandheatingwithreheat),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ControlSequenceOfOperation> for u8 {
fn from(val: ControlSequenceOfOperation) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum PresetScenario {
Occupied = 1,
Unoccupied = 2,
Sleep = 3,
Wake = 4,
Vacation = 5,
Goingtosleep = 6,
Userdefined = 254,
}
impl PresetScenario {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(PresetScenario::Occupied),
2 => Some(PresetScenario::Unoccupied),
3 => Some(PresetScenario::Sleep),
4 => Some(PresetScenario::Wake),
5 => Some(PresetScenario::Vacation),
6 => Some(PresetScenario::Goingtosleep),
254 => Some(PresetScenario::Userdefined),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<PresetScenario> for u8 {
fn from(val: PresetScenario) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum SetpointChangeSource {
Manual = 0,
Schedule = 1,
External = 2,
}
impl SetpointChangeSource {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(SetpointChangeSource::Manual),
1 => Some(SetpointChangeSource::Schedule),
2 => Some(SetpointChangeSource::External),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<SetpointChangeSource> for u8 {
fn from(val: SetpointChangeSource) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum SetpointRaiseLowerMode {
Heat = 0,
Cool = 1,
Both = 2,
}
impl SetpointRaiseLowerMode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(SetpointRaiseLowerMode::Heat),
1 => Some(SetpointRaiseLowerMode::Cool),
2 => Some(SetpointRaiseLowerMode::Both),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<SetpointRaiseLowerMode> for u8 {
fn from(val: SetpointRaiseLowerMode) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum StartOfWeek {
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
}
impl StartOfWeek {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(StartOfWeek::Sunday),
1 => Some(StartOfWeek::Monday),
2 => Some(StartOfWeek::Tuesday),
3 => Some(StartOfWeek::Wednesday),
4 => Some(StartOfWeek::Thursday),
5 => Some(StartOfWeek::Friday),
6 => Some(StartOfWeek::Saturday),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<StartOfWeek> for u8 {
fn from(val: StartOfWeek) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum SystemMode {
Off = 0,
Auto = 1,
Cool = 3,
Heat = 4,
Emergencyheat = 5,
Precooling = 6,
Fanonly = 7,
Dry = 8,
Sleep = 9,
}
impl SystemMode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(SystemMode::Off),
1 => Some(SystemMode::Auto),
3 => Some(SystemMode::Cool),
4 => Some(SystemMode::Heat),
5 => Some(SystemMode::Emergencyheat),
6 => Some(SystemMode::Precooling),
7 => Some(SystemMode::Fanonly),
8 => Some(SystemMode::Dry),
9 => Some(SystemMode::Sleep),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<SystemMode> for u8 {
fn from(val: SystemMode) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum TemperatureSetpointHold {
Setpointholdoff = 0,
Setpointholdon = 1,
}
impl TemperatureSetpointHold {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(TemperatureSetpointHold::Setpointholdoff),
1 => Some(TemperatureSetpointHold::Setpointholdon),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<TemperatureSetpointHold> for u8 {
fn from(val: TemperatureSetpointHold) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum ThermostatRunningMode {
Off = 0,
Cool = 3,
Heat = 4,
}
impl ThermostatRunningMode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(ThermostatRunningMode::Off),
3 => Some(ThermostatRunningMode::Cool),
4 => Some(ThermostatRunningMode::Heat),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<ThermostatRunningMode> for u8 {
fn from(val: ThermostatRunningMode) -> Self {
val as u8
}
}
pub type ACErrorCode = u8;
pub mod acerrorcode {
pub const COMPRESSOR_FAIL: u8 = 0x01;
pub const ROOM_SENSOR_FAIL: u8 = 0x02;
pub const OUTDOOR_SENSOR_FAIL: u8 = 0x04;
pub const COIL_SENSOR_FAIL: u8 = 0x08;
pub const FAN_FAIL: u8 = 0x10;
}
pub type Occupancy = u8;
pub mod occupancy {
pub const OCCUPIED: u8 = 0x01;
}
pub type PresetTypeFeatures = u8;
pub mod presettypefeatures {
pub const AUTOMATIC: u8 = 0x01;
pub const SUPPORTS_NAMES: u8 = 0x02;
}
pub type ProgrammingOperationMode = u8;
pub mod programmingoperationmode {
pub const SCHEDULE_ACTIVE: u8 = 0x01;
pub const AUTO_RECOVERY: u8 = 0x02;
pub const ECONOMY: u8 = 0x04;
}
pub type RelayState = u8;
pub mod relaystate {
pub const HEAT: u8 = 0x01;
pub const COOL: u8 = 0x02;
pub const FAN: u8 = 0x04;
pub const HEAT_STAGE2: u8 = 0x08;
pub const COOL_STAGE2: u8 = 0x10;
pub const FAN_STAGE2: u8 = 0x20;
pub const FAN_STAGE3: u8 = 0x40;
}
pub type RemoteSensing = u8;
pub mod remotesensing {
pub const LOCAL_TEMPERATURE: u8 = 0x01;
pub const OUTDOOR_TEMPERATURE: u8 = 0x02;
pub const OCCUPANCY: u8 = 0x04;
}
pub type ScheduleDayOfWeek = u8;
pub mod scheduledayofweek {
pub const SUNDAY: u8 = 0x01;
pub const MONDAY: u8 = 0x02;
pub const TUESDAY: u8 = 0x04;
pub const WEDNESDAY: u8 = 0x08;
pub const THURSDAY: u8 = 0x10;
pub const FRIDAY: u8 = 0x20;
pub const SATURDAY: u8 = 0x40;
pub const AWAY: u8 = 0x80;
}
pub type ScheduleMode = u8;
pub mod schedulemode {
pub const HEAT_SETPOINT_PRESENT: u8 = 0x01;
pub const COOL_SETPOINT_PRESENT: u8 = 0x02;
}
pub type ScheduleTypeFeatures = u8;
pub mod scheduletypefeatures {
pub const SUPPORTS_PRESETS: u8 = 0x01;
pub const SUPPORTS_SETPOINTS: u8 = 0x02;
pub const SUPPORTS_NAMES: u8 = 0x04;
pub const SUPPORTS_OFF: u8 = 0x08;
}
#[derive(Debug, serde::Serialize)]
pub struct Preset {
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub preset_handle: Option<Vec<u8>>,
pub preset_scenario: Option<PresetScenario>,
pub name: Option<String>,
pub cooling_setpoint: Option<i16>,
pub heating_setpoint: Option<i16>,
pub built_in: Option<bool>,
}
#[derive(Debug, serde::Serialize)]
pub struct PresetType {
pub preset_scenario: Option<PresetScenario>,
pub number_of_presets: Option<u8>,
pub preset_type_features: Option<PresetTypeFeatures>,
}
#[derive(Debug, serde::Serialize)]
pub struct Schedule {
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub schedule_handle: Option<Vec<u8>>,
pub system_mode: Option<SystemMode>,
pub name: Option<String>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub preset_handle: Option<Vec<u8>>,
pub transitions: Option<Vec<ScheduleTransition>>,
pub built_in: Option<bool>,
}
#[derive(Debug, serde::Serialize)]
pub struct ScheduleTransition {
pub day_of_week: Option<ScheduleDayOfWeek>,
pub transition_time: Option<u16>,
#[serde(serialize_with = "serialize_opt_bytes_as_hex")]
pub preset_handle: Option<Vec<u8>>,
pub system_mode: Option<SystemMode>,
pub cooling_setpoint: Option<i16>,
pub heating_setpoint: Option<i16>,
}
#[derive(Debug, serde::Serialize)]
pub struct ScheduleType {
pub system_mode: Option<SystemMode>,
pub number_of_schedules: Option<u8>,
pub schedule_type_features: Option<ScheduleTypeFeatures>,
}
#[derive(Debug, serde::Serialize)]
pub struct WeeklyScheduleTransition {
pub transition_time: Option<u16>,
pub heat_setpoint: Option<i16>,
pub cool_setpoint: Option<i16>,
}
pub fn encode_setpoint_raise_lower(mode: SetpointRaiseLowerMode, amount: i8) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt8(mode.to_u8())).into(),
(1, tlv::TlvItemValueEnc::Int8(amount)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_active_schedule_request(schedule_handle: Vec<u8>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::OctetString(schedule_handle)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_set_active_preset_request(preset_handle: Option<Vec<u8>>) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::OctetString(preset_handle.unwrap_or(vec![]))).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_local_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as i16))
} else {
Ok(None)
}
}
pub fn decode_outdoor_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as i16))
} else {
Ok(None)
}
}
pub fn decode_occupancy(inp: &tlv::TlvItemValue) -> anyhow::Result<Occupancy> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_abs_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_abs_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_abs_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_abs_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_pi_cooling_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_pi_heating_demand(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_hvac_system_type_configuration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_local_temperature_calibration(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_occupied_cooling_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_occupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_unoccupied_cooling_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_unoccupied_heating_setpoint(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_min_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_max_heat_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_min_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_max_cool_setpoint_limit(inp: &tlv::TlvItemValue) -> anyhow::Result<i16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as i16)
} else {
Err(anyhow::anyhow!("Expected Int16"))
}
}
pub fn decode_min_setpoint_dead_band(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_remote_sensing(inp: &tlv::TlvItemValue) -> anyhow::Result<RemoteSensing> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_control_sequence_of_operation(inp: &tlv::TlvItemValue) -> anyhow::Result<ControlSequenceOfOperation> {
if let tlv::TlvItemValue::Int(v) = inp {
ControlSequenceOfOperation::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_system_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<SystemMode> {
if let tlv::TlvItemValue::Int(v) = inp {
SystemMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_thermostat_running_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ThermostatRunningMode> {
if let tlv::TlvItemValue::Int(v) = inp {
ThermostatRunningMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_temperature_setpoint_hold(inp: &tlv::TlvItemValue) -> anyhow::Result<TemperatureSetpointHold> {
if let tlv::TlvItemValue::Int(v) = inp {
TemperatureSetpointHold::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_temperature_setpoint_hold_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as u16))
} else {
Ok(None)
}
}
pub fn decode_thermostat_programming_operation_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<ProgrammingOperationMode> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_thermostat_running_state(inp: &tlv::TlvItemValue) -> anyhow::Result<RelayState> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_setpoint_change_source(inp: &tlv::TlvItemValue) -> anyhow::Result<SetpointChangeSource> {
if let tlv::TlvItemValue::Int(v) = inp {
SetpointChangeSource::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_setpoint_change_amount(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as u8))
} else {
Ok(None)
}
}
pub fn decode_setpoint_change_source_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<u64> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v)
} else {
Err(anyhow::anyhow!("Expected UInt64"))
}
}
pub fn decode_occupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_occupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_occupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_unoccupied_setback(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_unoccupied_setback_min(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_unoccupied_setback_max(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_emergency_heat_delta(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_ac_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACType> {
if let tlv::TlvItemValue::Int(v) = inp {
ACType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_ac_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u16)
} else {
Err(anyhow::anyhow!("Expected UInt16"))
}
}
pub fn decode_ac_refrigerant_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACRefrigerantType> {
if let tlv::TlvItemValue::Int(v) = inp {
ACRefrigerantType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_ac_compressor_type(inp: &tlv::TlvItemValue) -> anyhow::Result<ACCompressorType> {
if let tlv::TlvItemValue::Int(v) = inp {
ACCompressorType::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_ac_error_code(inp: &tlv::TlvItemValue) -> anyhow::Result<ACErrorCode> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_aclouver_position(inp: &tlv::TlvItemValue) -> anyhow::Result<ACLouverPosition> {
if let tlv::TlvItemValue::Int(v) = inp {
ACLouverPosition::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_ac_coil_temperature(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as i16))
} else {
Ok(None)
}
}
pub fn decode_ac_capacity_format(inp: &tlv::TlvItemValue) -> anyhow::Result<ACCapacityFormat> {
if let tlv::TlvItemValue::Int(v) = inp {
ACCapacityFormat::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
} else {
Err(anyhow::anyhow!("Expected Integer"))
}
}
pub fn decode_preset_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<PresetType>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(PresetType {
preset_scenario: item.get_int(&[0]).and_then(|v| PresetScenario::from_u8(v as u8)),
number_of_presets: item.get_int(&[1]).map(|v| v as u8),
preset_type_features: item.get_int(&[2]).map(|v| v as u8),
});
}
}
Ok(res)
}
pub fn decode_schedule_types(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ScheduleType>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(ScheduleType {
system_mode: item.get_int(&[0]).and_then(|v| SystemMode::from_u8(v as u8)),
number_of_schedules: item.get_int(&[1]).map(|v| v as u8),
schedule_type_features: item.get_int(&[2]).map(|v| v as u8),
});
}
}
Ok(res)
}
pub fn decode_number_of_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_number_of_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_number_of_schedule_transitions(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(*v as u8)
} else {
Err(anyhow::anyhow!("Expected UInt8"))
}
}
pub fn decode_number_of_schedule_transition_per_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v as u8))
} else {
Ok(None)
}
}
pub fn decode_active_preset_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
if let tlv::TlvItemValue::OctetString(v) = inp {
Ok(Some(v.clone()))
} else {
Ok(None)
}
}
pub fn decode_active_schedule_handle(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Vec<u8>>> {
if let tlv::TlvItemValue::OctetString(v) = inp {
Ok(Some(v.clone()))
} else {
Ok(None)
}
}
pub fn decode_presets(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Preset>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(Preset {
preset_handle: item.get_octet_string_owned(&[0]),
preset_scenario: item.get_int(&[1]).and_then(|v| PresetScenario::from_u8(v as u8)),
name: item.get_string_owned(&[2]),
cooling_setpoint: item.get_int(&[3]).map(|v| v as i16),
heating_setpoint: item.get_int(&[4]).map(|v| v as i16),
built_in: item.get_bool(&[5]),
});
}
}
Ok(res)
}
pub fn decode_schedules(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Schedule>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(Schedule {
schedule_handle: item.get_octet_string_owned(&[0]),
system_mode: item.get_int(&[1]).and_then(|v| SystemMode::from_u8(v as u8)),
name: item.get_string_owned(&[2]),
preset_handle: item.get_octet_string_owned(&[3]),
transitions: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
let mut items = Vec::new();
for list_item in l {
items.push(ScheduleTransition {
day_of_week: list_item.get_int(&[0]).map(|v| v as u8),
transition_time: list_item.get_int(&[1]).map(|v| v as u16),
preset_handle: list_item.get_octet_string_owned(&[2]),
system_mode: list_item.get_int(&[3]).and_then(|v| SystemMode::from_u8(v as u8)),
cooling_setpoint: list_item.get_int(&[4]).map(|v| v as i16),
heating_setpoint: list_item.get_int(&[5]).map(|v| v as i16),
});
}
Some(items)
} else {
None
}
},
built_in: item.get_bool(&[5]),
});
}
}
Ok(res)
}
pub fn decode_setpoint_hold_expiry_timestamp(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v))
} else {
Ok(None)
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0201 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0201, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_local_temperature(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_outdoor_temperature(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_occupancy(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_abs_min_heat_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_abs_max_heat_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_abs_min_cool_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_abs_max_cool_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_pi_cooling_demand(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_pi_heating_demand(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_hvac_system_type_configuration(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0010 => {
match decode_local_temperature_calibration(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0011 => {
match decode_occupied_cooling_setpoint(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0012 => {
match decode_occupied_heating_setpoint(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0013 => {
match decode_unoccupied_cooling_setpoint(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0014 => {
match decode_unoccupied_heating_setpoint(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0015 => {
match decode_min_heat_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0016 => {
match decode_max_heat_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0017 => {
match decode_min_cool_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0018 => {
match decode_max_cool_setpoint_limit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0019 => {
match decode_min_setpoint_dead_band(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001A => {
match decode_remote_sensing(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001B => {
match decode_control_sequence_of_operation(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001C => {
match decode_system_mode(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x001E => {
match decode_thermostat_running_mode(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0023 => {
match decode_temperature_setpoint_hold(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0024 => {
match decode_temperature_setpoint_hold_duration(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0025 => {
match decode_thermostat_programming_operation_mode(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0029 => {
match decode_thermostat_running_state(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0030 => {
match decode_setpoint_change_source(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0031 => {
match decode_setpoint_change_amount(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0032 => {
match decode_setpoint_change_source_timestamp(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0034 => {
match decode_occupied_setback(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0035 => {
match decode_occupied_setback_min(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0036 => {
match decode_occupied_setback_max(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0037 => {
match decode_unoccupied_setback(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0038 => {
match decode_unoccupied_setback_min(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0039 => {
match decode_unoccupied_setback_max(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x003A => {
match decode_emergency_heat_delta(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0040 => {
match decode_ac_type(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0041 => {
match decode_ac_capacity(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0042 => {
match decode_ac_refrigerant_type(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0043 => {
match decode_ac_compressor_type(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0044 => {
match decode_ac_error_code(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0045 => {
match decode_aclouver_position(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0046 => {
match decode_ac_coil_temperature(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0047 => {
match decode_ac_capacity_format(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0048 => {
match decode_preset_types(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0049 => {
match decode_schedule_types(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x004A => {
match decode_number_of_presets(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x004B => {
match decode_number_of_schedules(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x004C => {
match decode_number_of_schedule_transitions(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x004D => {
match decode_number_of_schedule_transition_per_day(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x004E => {
match decode_active_preset_handle(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x004F => {
match decode_active_schedule_handle(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0050 => {
match decode_presets(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0051 => {
match decode_schedules(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0052 => {
match decode_setpoint_hold_expiry_timestamp(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
_ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
}
}
pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
vec![
(0x0000, "LocalTemperature"),
(0x0001, "OutdoorTemperature"),
(0x0002, "Occupancy"),
(0x0003, "AbsMinHeatSetpointLimit"),
(0x0004, "AbsMaxHeatSetpointLimit"),
(0x0005, "AbsMinCoolSetpointLimit"),
(0x0006, "AbsMaxCoolSetpointLimit"),
(0x0007, "PICoolingDemand"),
(0x0008, "PIHeatingDemand"),
(0x0009, "HVACSystemTypeConfiguration"),
(0x0010, "LocalTemperatureCalibration"),
(0x0011, "OccupiedCoolingSetpoint"),
(0x0012, "OccupiedHeatingSetpoint"),
(0x0013, "UnoccupiedCoolingSetpoint"),
(0x0014, "UnoccupiedHeatingSetpoint"),
(0x0015, "MinHeatSetpointLimit"),
(0x0016, "MaxHeatSetpointLimit"),
(0x0017, "MinCoolSetpointLimit"),
(0x0018, "MaxCoolSetpointLimit"),
(0x0019, "MinSetpointDeadBand"),
(0x001A, "RemoteSensing"),
(0x001B, "ControlSequenceOfOperation"),
(0x001C, "SystemMode"),
(0x001E, "ThermostatRunningMode"),
(0x0023, "TemperatureSetpointHold"),
(0x0024, "TemperatureSetpointHoldDuration"),
(0x0025, "ThermostatProgrammingOperationMode"),
(0x0029, "ThermostatRunningState"),
(0x0030, "SetpointChangeSource"),
(0x0031, "SetpointChangeAmount"),
(0x0032, "SetpointChangeSourceTimestamp"),
(0x0034, "OccupiedSetback"),
(0x0035, "OccupiedSetbackMin"),
(0x0036, "OccupiedSetbackMax"),
(0x0037, "UnoccupiedSetback"),
(0x0038, "UnoccupiedSetbackMin"),
(0x0039, "UnoccupiedSetbackMax"),
(0x003A, "EmergencyHeatDelta"),
(0x0040, "ACType"),
(0x0041, "ACCapacity"),
(0x0042, "ACRefrigerantType"),
(0x0043, "ACCompressorType"),
(0x0044, "ACErrorCode"),
(0x0045, "ACLouverPosition"),
(0x0046, "ACCoilTemperature"),
(0x0047, "ACCapacityFormat"),
(0x0048, "PresetTypes"),
(0x0049, "ScheduleTypes"),
(0x004A, "NumberOfPresets"),
(0x004B, "NumberOfSchedules"),
(0x004C, "NumberOfScheduleTransitions"),
(0x004D, "NumberOfScheduleTransitionPerDay"),
(0x004E, "ActivePresetHandle"),
(0x004F, "ActiveScheduleHandle"),
(0x0050, "Presets"),
(0x0051, "Schedules"),
(0x0052, "SetpointHoldExpiryTimestamp"),
]
}