#![allow(clippy::too_many_arguments)]
use crate::tlv;
use anyhow;
use serde_json;
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum AuxiliaryLoadSetting {
Off = 0,
On = 1,
None = 2,
}
impl AuxiliaryLoadSetting {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(AuxiliaryLoadSetting::Off),
1 => Some(AuxiliaryLoadSetting::On),
2 => Some(AuxiliaryLoadSetting::None),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<AuxiliaryLoadSetting> for u8 {
fn from(val: AuxiliaryLoadSetting) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum BlockMode {
Noblock = 0,
Combined = 1,
Individual = 2,
}
impl BlockMode {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(BlockMode::Noblock),
1 => Some(BlockMode::Combined),
2 => Some(BlockMode::Individual),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<BlockMode> for u8 {
fn from(val: BlockMode) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DayEntryRandomizationType {
None = 0,
Fixed = 1,
Random = 2,
Randompositive = 3,
Randomnegative = 4,
}
impl DayEntryRandomizationType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(DayEntryRandomizationType::None),
1 => Some(DayEntryRandomizationType::Fixed),
2 => Some(DayEntryRandomizationType::Random),
3 => Some(DayEntryRandomizationType::Randompositive),
4 => Some(DayEntryRandomizationType::Randomnegative),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DayEntryRandomizationType> for u8 {
fn from(val: DayEntryRandomizationType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum DayType {
Standard = 0,
Holiday = 1,
Dynamic = 2,
Event = 3,
}
impl DayType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(DayType::Standard),
1 => Some(DayType::Holiday),
2 => Some(DayType::Dynamic),
3 => Some(DayType::Event),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<DayType> for u8 {
fn from(val: DayType) -> Self {
val as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum PeakPeriodSeverity {
Unused = 0,
Low = 1,
Medium = 2,
High = 3,
}
impl PeakPeriodSeverity {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(PeakPeriodSeverity::Unused),
1 => Some(PeakPeriodSeverity::Low),
2 => Some(PeakPeriodSeverity::Medium),
3 => Some(PeakPeriodSeverity::High),
_ => None,
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
}
impl From<PeakPeriodSeverity> for u8 {
fn from(val: PeakPeriodSeverity) -> Self {
val as u8
}
}
pub type DayPatternDayOfWeek = u8;
pub mod daypatterndayofweek {
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;
}
#[derive(Debug, serde::Serialize)]
pub struct AuxiliaryLoadSwitchSettings {
pub number: Option<u8>,
pub required_state: Option<AuxiliaryLoadSetting>,
}
#[derive(Debug, serde::Serialize)]
pub struct AuxiliaryLoadSwitchesSettings {
pub switch_states: Option<Vec<AuxiliaryLoadSwitchSettings>>,
}
#[derive(Debug, serde::Serialize)]
pub struct CalendarPeriod {
pub start_date: Option<u64>,
pub day_pattern_i_ds: Option<Vec<u32>>,
}
#[derive(Debug, serde::Serialize)]
pub struct DayEntry {
pub day_entry_id: Option<u32>,
pub start_time: Option<u16>,
pub duration: Option<u16>,
pub randomization_offset: Option<i16>,
pub randomization_type: Option<DayEntryRandomizationType>,
}
#[derive(Debug, serde::Serialize)]
pub struct DayPattern {
pub day_pattern_id: Option<u32>,
pub days_of_week: Option<DayPatternDayOfWeek>,
pub day_entry_i_ds: Option<Vec<u32>>,
}
#[derive(Debug, serde::Serialize)]
pub struct Day {
pub date: Option<u64>,
pub day_type: Option<DayType>,
pub day_entry_i_ds: Option<Vec<u32>>,
}
#[derive(Debug, serde::Serialize)]
pub struct PeakPeriod {
pub severity: Option<PeakPeriodSeverity>,
pub peak_period: Option<u16>,
}
#[derive(Debug, serde::Serialize)]
pub struct TariffComponent {
pub tariff_component_id: Option<u32>,
pub price: Option<TariffPrice>,
pub friendly_credit: Option<bool>,
pub auxiliary_load: Option<AuxiliaryLoadSwitchSettings>,
pub peak_period: Option<PeakPeriod>,
pub threshold: Option<i64>,
pub label: Option<String>,
pub predicted: Option<bool>,
}
#[derive(Debug, serde::Serialize)]
pub struct TariffInformation {
pub tariff_label: Option<String>,
pub provider_name: Option<String>,
pub block_mode: Option<BlockMode>,
}
#[derive(Debug, serde::Serialize)]
pub struct TariffPeriod {
pub label: Option<String>,
pub day_entry_i_ds: Option<Vec<u32>>,
pub tariff_component_i_ds: Option<Vec<u32>>,
}
#[derive(Debug, serde::Serialize)]
pub struct TariffPrice {
pub price_type: Option<u8>,
pub price: Option<u8>,
pub price_level: Option<i16>,
}
pub fn encode_get_tariff_component(tariff_component_id: u32) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt32(tariff_component_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn encode_get_day_entry(day_entry_id: u32) -> anyhow::Result<Vec<u8>> {
let tlv = tlv::TlvItemEnc {
tag: 0,
value: tlv::TlvItemValueEnc::StructInvisible(vec![
(0, tlv::TlvItemValueEnc::UInt32(day_entry_id)).into(),
]),
};
Ok(tlv.encode()?)
}
pub fn decode_tariff_info(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<TariffInformation>> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(Some(TariffInformation {
tariff_label: item.get_string_owned(&[0]),
provider_name: item.get_string_owned(&[1]),
block_mode: item.get_int(&[3]).and_then(|v| BlockMode::from_u8(v as u8)),
}))
} else {
Ok(None)
}
}
pub fn decode_tariff_unit(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_start_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v))
} else {
Ok(None)
}
}
pub fn decode_day_entries(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DayEntry>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DayEntry {
day_entry_id: item.get_int(&[0]).map(|v| v as u32),
start_time: item.get_int(&[1]).map(|v| v as u16),
duration: item.get_int(&[2]).map(|v| v as u16),
randomization_offset: item.get_int(&[3]).map(|v| v as i16),
randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
});
}
}
Ok(res)
}
pub fn decode_day_patterns(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DayPattern>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(DayPattern {
day_pattern_id: item.get_int(&[0]).map(|v| v as u32),
days_of_week: item.get_int(&[1]).map(|v| v as u8),
day_entry_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_calendar_periods(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CalendarPeriod>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(CalendarPeriod {
start_date: item.get_int(&[0]),
day_pattern_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_individual_days(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Day>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(Day {
date: item.get_int(&[0]),
day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
day_entry_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_current_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(Some(Day {
date: item.get_int(&[0]),
day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
day_entry_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
}))
} else {
Ok(None)
}
}
pub fn decode_next_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(Some(Day {
date: item.get_int(&[0]),
day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
day_entry_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
}))
} else {
Ok(None)
}
}
pub fn decode_current_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(Some(DayEntry {
day_entry_id: item.get_int(&[0]).map(|v| v as u32),
start_time: item.get_int(&[1]).map(|v| v as u16),
duration: item.get_int(&[2]).map(|v| v as u16),
randomization_offset: item.get_int(&[3]).map(|v| v as i16),
randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
}))
} else {
Ok(None)
}
}
pub fn decode_current_day_entry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v))
} else {
Ok(None)
}
}
pub fn decode_next_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(Some(DayEntry {
day_entry_id: item.get_int(&[0]).map(|v| v as u32),
start_time: item.get_int(&[1]).map(|v| v as u16),
duration: item.get_int(&[2]).map(|v| v as u16),
randomization_offset: item.get_int(&[3]).map(|v| v as i16),
randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
}))
} else {
Ok(None)
}
}
pub fn decode_next_day_entry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(Some(*v))
} else {
Ok(None)
}
}
pub fn decode_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(TariffComponent {
tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
price: {
if let Some(nested_tlv) = item.get(&[1]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
Some(TariffPrice {
price_type: nested_item.get_int(&[0]).map(|v| v as u8),
price: nested_item.get_int(&[1]).map(|v| v as u8),
price_level: nested_item.get_int(&[2]).map(|v| v as i16),
})
} else {
None
}
} else {
None
}
},
friendly_credit: item.get_bool(&[2]),
auxiliary_load: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(AuxiliaryLoadSwitchSettings {
number: nested_item.get_int(&[0]).map(|v| v as u8),
required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
})
} else {
None
}
} else {
None
}
},
peak_period: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(PeakPeriod {
severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
threshold: item.get_int(&[6]).map(|v| v as i64),
label: item.get_string_owned(&[7]),
predicted: item.get_bool(&[8]),
});
}
}
Ok(res)
}
pub fn decode_tariff_periods(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffPeriod>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(TariffPeriod {
label: item.get_string_owned(&[0]),
day_entry_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
tariff_component_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
});
}
}
Ok(res)
}
pub fn decode_current_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(TariffComponent {
tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
price: {
if let Some(nested_tlv) = item.get(&[1]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
Some(TariffPrice {
price_type: nested_item.get_int(&[0]).map(|v| v as u8),
price: nested_item.get_int(&[1]).map(|v| v as u8),
price_level: nested_item.get_int(&[2]).map(|v| v as i16),
})
} else {
None
}
} else {
None
}
},
friendly_credit: item.get_bool(&[2]),
auxiliary_load: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(AuxiliaryLoadSwitchSettings {
number: nested_item.get_int(&[0]).map(|v| v as u8),
required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
})
} else {
None
}
} else {
None
}
},
peak_period: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(PeakPeriod {
severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
threshold: item.get_int(&[6]).map(|v| v as i64),
label: item.get_string_owned(&[7]),
predicted: item.get_bool(&[8]),
});
}
}
Ok(res)
}
pub fn decode_next_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
let mut res = Vec::new();
if let tlv::TlvItemValue::List(v) = inp {
for item in v {
res.push(TariffComponent {
tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
price: {
if let Some(nested_tlv) = item.get(&[1]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
Some(TariffPrice {
price_type: nested_item.get_int(&[0]).map(|v| v as u8),
price: nested_item.get_int(&[1]).map(|v| v as u8),
price_level: nested_item.get_int(&[2]).map(|v| v as i16),
})
} else {
None
}
} else {
None
}
},
friendly_credit: item.get_bool(&[2]),
auxiliary_load: {
if let Some(nested_tlv) = item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(AuxiliaryLoadSwitchSettings {
number: nested_item.get_int(&[0]).map(|v| v as u8),
required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
})
} else {
None
}
} else {
None
}
},
peak_period: {
if let Some(nested_tlv) = item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(PeakPeriod {
severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
threshold: item.get_int(&[6]).map(|v| v as i64),
label: item.get_string_owned(&[7]),
predicted: item.get_bool(&[8]),
});
}
}
Ok(res)
}
pub fn decode_default_randomization_offset(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_default_randomization_type(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntryRandomizationType>> {
if let tlv::TlvItemValue::Int(v) = inp {
Ok(DayEntryRandomizationType::from_u8(*v as u8))
} else {
Ok(None)
}
}
pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
if cluster_id != 0x0700 {
return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0700, got {}\"}}", cluster_id);
}
match attribute_id {
0x0000 => {
match decode_tariff_info(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0001 => {
match decode_tariff_unit(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0002 => {
match decode_start_date(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0003 => {
match decode_day_entries(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0004 => {
match decode_day_patterns(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0005 => {
match decode_calendar_periods(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0006 => {
match decode_individual_days(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0007 => {
match decode_current_day(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0008 => {
match decode_next_day(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0009 => {
match decode_current_day_entry(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000A => {
match decode_current_day_entry_date(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000B => {
match decode_next_day_entry(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000C => {
match decode_next_day_entry_date(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000D => {
match decode_tariff_components(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000E => {
match decode_tariff_periods(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x000F => {
match decode_current_tariff_components(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0010 => {
match decode_next_tariff_components(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0011 => {
match decode_default_randomization_offset(tlv_value) {
Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
Err(e) => format!("{{\"error\": \"{}\"}}", e),
}
}
0x0012 => {
match decode_default_randomization_type(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, "TariffInfo"),
(0x0001, "TariffUnit"),
(0x0002, "StartDate"),
(0x0003, "DayEntries"),
(0x0004, "DayPatterns"),
(0x0005, "CalendarPeriods"),
(0x0006, "IndividualDays"),
(0x0007, "CurrentDay"),
(0x0008, "NextDay"),
(0x0009, "CurrentDayEntry"),
(0x000A, "CurrentDayEntryDate"),
(0x000B, "NextDayEntry"),
(0x000C, "NextDayEntryDate"),
(0x000D, "TariffComponents"),
(0x000E, "TariffPeriods"),
(0x000F, "CurrentTariffComponents"),
(0x0010, "NextTariffComponents"),
(0x0011, "DefaultRandomizationOffset"),
(0x0012, "DefaultRandomizationType"),
]
}
pub fn get_command_list() -> Vec<(u32, &'static str)> {
vec![
(0x00, "GetTariffComponent"),
(0x01, "GetDayEntry"),
]
}
pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
match cmd_id {
0x00 => Some("GetTariffComponent"),
0x01 => Some("GetDayEntry"),
_ => None,
}
}
pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
match cmd_id {
0x00 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "tariff_component_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
0x01 => Some(vec![
crate::clusters::codec::CommandField { tag: 0, name: "day_entry_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
]),
_ => None,
}
}
pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
match cmd_id {
0x00 => {
let tariff_component_id = crate::clusters::codec::json_util::get_u32(args, "tariff_component_id")?;
encode_get_tariff_component(tariff_component_id)
}
0x01 => {
let day_entry_id = crate::clusters::codec::json_util::get_u32(args, "day_entry_id")?;
encode_get_day_entry(day_entry_id)
}
_ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
}
}
#[derive(Debug, serde::Serialize)]
pub struct GetTariffComponentResponse {
pub label: Option<String>,
pub day_entry_i_ds: Option<Vec<u32>>,
pub tariff_component: Option<TariffComponent>,
}
#[derive(Debug, serde::Serialize)]
pub struct GetDayEntryResponse {
pub day_entry: Option<DayEntry>,
}
pub fn decode_get_tariff_component_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetTariffComponentResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(GetTariffComponentResponse {
label: item.get_string_owned(&[0]),
day_entry_i_ds: {
if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
Some(items)
} else {
None
}
},
tariff_component: {
if let Some(nested_tlv) = item.get(&[2]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
Some(TariffComponent {
tariff_component_id: nested_item.get_int(&[0]).map(|v| v as u32),
price: {
if let Some(nested_tlv) = nested_item.get(&[1]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
Some(TariffPrice {
price_type: nested_item.get_int(&[0]).map(|v| v as u8),
price: nested_item.get_int(&[1]).map(|v| v as u8),
price_level: nested_item.get_int(&[2]).map(|v| v as i16),
})
} else {
None
}
} else {
None
}
},
friendly_credit: nested_item.get_bool(&[2]),
auxiliary_load: {
if let Some(nested_tlv) = nested_item.get(&[3]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
Some(AuxiliaryLoadSwitchSettings {
number: nested_item.get_int(&[0]).map(|v| v as u8),
required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
})
} else {
None
}
} else {
None
}
},
peak_period: {
if let Some(nested_tlv) = nested_item.get(&[4]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
Some(PeakPeriod {
severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
})
} else {
None
}
} else {
None
}
},
threshold: nested_item.get_int(&[6]).map(|v| v as i64),
label: nested_item.get_string_owned(&[7]),
predicted: nested_item.get_bool(&[8]),
})
} else {
None
}
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub fn decode_get_day_entry_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetDayEntryResponse> {
if let tlv::TlvItemValue::List(_fields) = inp {
let item = tlv::TlvItem { tag: 0, value: inp.clone() };
Ok(GetDayEntryResponse {
day_entry: {
if let Some(nested_tlv) = item.get(&[0]) {
if let tlv::TlvItemValue::List(_) = nested_tlv {
let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
Some(DayEntry {
day_entry_id: nested_item.get_int(&[0]).map(|v| v as u32),
start_time: nested_item.get_int(&[1]).map(|v| v as u16),
duration: nested_item.get_int(&[2]).map(|v| v as u16),
randomization_offset: nested_item.get_int(&[3]).map(|v| v as i16),
randomization_type: nested_item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
})
} else {
None
}
} else {
None
}
},
})
} else {
Err(anyhow::anyhow!("Expected struct fields"))
}
}
pub async fn get_tariff_component(conn: &crate::controller::Connection, endpoint: u16, tariff_component_id: u32) -> anyhow::Result<GetTariffComponentResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_CMD_ID_GETTARIFFCOMPONENT, &encode_get_tariff_component(tariff_component_id)?).await?;
decode_get_tariff_component_response(&tlv)
}
pub async fn get_day_entry(conn: &crate::controller::Connection, endpoint: u16, day_entry_id: u32) -> anyhow::Result<GetDayEntryResponse> {
let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_CMD_ID_GETDAYENTRY, &encode_get_day_entry(day_entry_id)?).await?;
decode_get_day_entry_response(&tlv)
}
pub async fn read_tariff_info(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<TariffInformation>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFINFO).await?;
decode_tariff_info(&tlv)
}
pub async fn read_tariff_unit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFUNIT).await?;
decode_tariff_unit(&tlv)
}
pub async fn read_start_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_STARTDATE).await?;
decode_start_date(&tlv)
}
pub async fn read_day_entries(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DayEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DAYENTRIES).await?;
decode_day_entries(&tlv)
}
pub async fn read_day_patterns(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DayPattern>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DAYPATTERNS).await?;
decode_day_patterns(&tlv)
}
pub async fn read_calendar_periods(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<CalendarPeriod>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CALENDARPERIODS).await?;
decode_calendar_periods(&tlv)
}
pub async fn read_individual_days(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Day>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_INDIVIDUALDAYS).await?;
decode_individual_days(&tlv)
}
pub async fn read_current_day(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Day>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTDAY).await?;
decode_current_day(&tlv)
}
pub async fn read_next_day(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Day>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTDAY).await?;
decode_next_day(&tlv)
}
pub async fn read_current_day_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DayEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTDAYENTRY).await?;
decode_current_day_entry(&tlv)
}
pub async fn read_current_day_entry_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTDAYENTRYDATE).await?;
decode_current_day_entry_date(&tlv)
}
pub async fn read_next_day_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DayEntry>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTDAYENTRY).await?;
decode_next_day_entry(&tlv)
}
pub async fn read_next_day_entry_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTDAYENTRYDATE).await?;
decode_next_day_entry_date(&tlv)
}
pub async fn read_tariff_components(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffComponent>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFCOMPONENTS).await?;
decode_tariff_components(&tlv)
}
pub async fn read_tariff_periods(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffPeriod>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFPERIODS).await?;
decode_tariff_periods(&tlv)
}
pub async fn read_current_tariff_components(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffComponent>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTTARIFFCOMPONENTS).await?;
decode_current_tariff_components(&tlv)
}
pub async fn read_next_tariff_components(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffComponent>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTTARIFFCOMPONENTS).await?;
decode_next_tariff_components(&tlv)
}
pub async fn read_default_randomization_offset(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i16>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DEFAULTRANDOMIZATIONOFFSET).await?;
decode_default_randomization_offset(&tlv)
}
pub async fn read_default_randomization_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DayEntryRandomizationType>> {
let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DEFAULTRANDOMIZATIONTYPE).await?;
decode_default_randomization_type(&tlv)
}