matc/clusters/codec/
commodity_tariff.rs

1//! Matter TLV encoders and decoders for Commodity Tariff Cluster
2//! Cluster ID: 0x0700
3//!
4//! This file is automatically generated from CommodityTariff.xml
5
6#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13// Enum definitions
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum AuxiliaryLoadSetting {
18    /// The switch should be in the OFF state
19    Off = 0,
20    /// The switch should be in the ON state
21    On = 1,
22    /// No state is required
23    None = 2,
24}
25
26impl AuxiliaryLoadSetting {
27    /// Convert from u8 value
28    pub fn from_u8(value: u8) -> Option<Self> {
29        match value {
30            0 => Some(AuxiliaryLoadSetting::Off),
31            1 => Some(AuxiliaryLoadSetting::On),
32            2 => Some(AuxiliaryLoadSetting::None),
33            _ => None,
34        }
35    }
36
37    /// Convert to u8 value
38    pub fn to_u8(self) -> u8 {
39        self as u8
40    }
41}
42
43impl From<AuxiliaryLoadSetting> for u8 {
44    fn from(val: AuxiliaryLoadSetting) -> Self {
45        val as u8
46    }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
50#[repr(u8)]
51pub enum BlockMode {
52    /// Tariff has no usage blocks
53    Noblock = 0,
54    /// Usage is metered in combined blocks
55    Combined = 1,
56    /// Usage is metered separately by tariff component
57    Individual = 2,
58}
59
60impl BlockMode {
61    /// Convert from u8 value
62    pub fn from_u8(value: u8) -> Option<Self> {
63        match value {
64            0 => Some(BlockMode::Noblock),
65            1 => Some(BlockMode::Combined),
66            2 => Some(BlockMode::Individual),
67            _ => None,
68        }
69    }
70
71    /// Convert to u8 value
72    pub fn to_u8(self) -> u8 {
73        self as u8
74    }
75}
76
77impl From<BlockMode> for u8 {
78    fn from(val: BlockMode) -> Self {
79        val as u8
80    }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
84#[repr(u8)]
85pub enum DayEntryRandomizationType {
86    /// No randomization applied
87    None = 0,
88    /// An unchanging offset
89    Fixed = 1,
90    /// A random value
91    Random = 2,
92    /// A random positive value
93    Randompositive = 3,
94    /// A random negative value
95    Randomnegative = 4,
96}
97
98impl DayEntryRandomizationType {
99    /// Convert from u8 value
100    pub fn from_u8(value: u8) -> Option<Self> {
101        match value {
102            0 => Some(DayEntryRandomizationType::None),
103            1 => Some(DayEntryRandomizationType::Fixed),
104            2 => Some(DayEntryRandomizationType::Random),
105            3 => Some(DayEntryRandomizationType::Randompositive),
106            4 => Some(DayEntryRandomizationType::Randomnegative),
107            _ => None,
108        }
109    }
110
111    /// Convert to u8 value
112    pub fn to_u8(self) -> u8 {
113        self as u8
114    }
115}
116
117impl From<DayEntryRandomizationType> for u8 {
118    fn from(val: DayEntryRandomizationType) -> Self {
119        val as u8
120    }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
124#[repr(u8)]
125pub enum DayType {
126    /// Standard
127    Standard = 0,
128    /// Holiday
129    Holiday = 1,
130    /// Dynamic Pricing
131    Dynamic = 2,
132    /// Individual Events
133    Event = 3,
134}
135
136impl DayType {
137    /// Convert from u8 value
138    pub fn from_u8(value: u8) -> Option<Self> {
139        match value {
140            0 => Some(DayType::Standard),
141            1 => Some(DayType::Holiday),
142            2 => Some(DayType::Dynamic),
143            3 => Some(DayType::Event),
144            _ => None,
145        }
146    }
147
148    /// Convert to u8 value
149    pub fn to_u8(self) -> u8 {
150        self as u8
151    }
152}
153
154impl From<DayType> for u8 {
155    fn from(val: DayType) -> Self {
156        val as u8
157    }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
161#[repr(u8)]
162pub enum PeakPeriodSeverity {
163    /// Unused
164    Unused = 0,
165    /// Low
166    Low = 1,
167    /// Medium
168    Medium = 2,
169    /// High
170    High = 3,
171}
172
173impl PeakPeriodSeverity {
174    /// Convert from u8 value
175    pub fn from_u8(value: u8) -> Option<Self> {
176        match value {
177            0 => Some(PeakPeriodSeverity::Unused),
178            1 => Some(PeakPeriodSeverity::Low),
179            2 => Some(PeakPeriodSeverity::Medium),
180            3 => Some(PeakPeriodSeverity::High),
181            _ => None,
182        }
183    }
184
185    /// Convert to u8 value
186    pub fn to_u8(self) -> u8 {
187        self as u8
188    }
189}
190
191impl From<PeakPeriodSeverity> for u8 {
192    fn from(val: PeakPeriodSeverity) -> Self {
193        val as u8
194    }
195}
196
197// Bitmap definitions
198
199/// DayPatternDayOfWeek bitmap type
200pub type DayPatternDayOfWeek = u8;
201
202/// Constants for DayPatternDayOfWeek
203pub mod daypatterndayofweek {
204    /// Sunday
205    pub const SUNDAY: u8 = 0x01;
206    /// Monday
207    pub const MONDAY: u8 = 0x02;
208    /// Tuesday
209    pub const TUESDAY: u8 = 0x04;
210    /// Wednesday
211    pub const WEDNESDAY: u8 = 0x08;
212    /// Thursday
213    pub const THURSDAY: u8 = 0x10;
214    /// Friday
215    pub const FRIDAY: u8 = 0x20;
216    /// Saturday
217    pub const SATURDAY: u8 = 0x40;
218}
219
220// Struct definitions
221
222#[derive(Debug, serde::Serialize)]
223pub struct AuxiliaryLoadSwitchSettings {
224    pub number: Option<u8>,
225    pub required_state: Option<AuxiliaryLoadSetting>,
226}
227
228#[derive(Debug, serde::Serialize)]
229pub struct AuxiliaryLoadSwitchesSettings {
230    pub switch_states: Option<Vec<AuxiliaryLoadSwitchSettings>>,
231}
232
233#[derive(Debug, serde::Serialize)]
234pub struct CalendarPeriod {
235    pub start_date: Option<u64>,
236    pub day_pattern_i_ds: Option<Vec<u32>>,
237}
238
239#[derive(Debug, serde::Serialize)]
240pub struct DayEntry {
241    pub day_entry_id: Option<u32>,
242    pub start_time: Option<u16>,
243    pub duration: Option<u16>,
244    pub randomization_offset: Option<i16>,
245    pub randomization_type: Option<DayEntryRandomizationType>,
246}
247
248#[derive(Debug, serde::Serialize)]
249pub struct DayPattern {
250    pub day_pattern_id: Option<u32>,
251    pub days_of_week: Option<DayPatternDayOfWeek>,
252    pub day_entry_i_ds: Option<Vec<u32>>,
253}
254
255#[derive(Debug, serde::Serialize)]
256pub struct Day {
257    pub date: Option<u64>,
258    pub day_type: Option<DayType>,
259    pub day_entry_i_ds: Option<Vec<u32>>,
260}
261
262#[derive(Debug, serde::Serialize)]
263pub struct PeakPeriod {
264    pub severity: Option<PeakPeriodSeverity>,
265    pub peak_period: Option<u16>,
266}
267
268#[derive(Debug, serde::Serialize)]
269pub struct TariffComponent {
270    pub tariff_component_id: Option<u32>,
271    pub price: Option<TariffPrice>,
272    pub friendly_credit: Option<bool>,
273    pub auxiliary_load: Option<AuxiliaryLoadSwitchSettings>,
274    pub peak_period: Option<PeakPeriod>,
275    pub threshold: Option<i64>,
276    pub label: Option<String>,
277    pub predicted: Option<bool>,
278}
279
280#[derive(Debug, serde::Serialize)]
281pub struct TariffInformation {
282    pub tariff_label: Option<String>,
283    pub provider_name: Option<String>,
284    pub block_mode: Option<BlockMode>,
285}
286
287#[derive(Debug, serde::Serialize)]
288pub struct TariffPeriod {
289    pub label: Option<String>,
290    pub day_entry_i_ds: Option<Vec<u32>>,
291    pub tariff_component_i_ds: Option<Vec<u32>>,
292}
293
294#[derive(Debug, serde::Serialize)]
295pub struct TariffPrice {
296    pub price_type: Option<u8>,
297    pub price: Option<u8>,
298    pub price_level: Option<i16>,
299}
300
301// Command encoders
302
303/// Encode GetTariffComponent command (0x00)
304pub fn encode_get_tariff_component(tariff_component_id: u32) -> anyhow::Result<Vec<u8>> {
305    let tlv = tlv::TlvItemEnc {
306        tag: 0,
307        value: tlv::TlvItemValueEnc::StructInvisible(vec![
308        (0, tlv::TlvItemValueEnc::UInt32(tariff_component_id)).into(),
309        ]),
310    };
311    Ok(tlv.encode()?)
312}
313
314/// Encode GetDayEntry command (0x01)
315pub fn encode_get_day_entry(day_entry_id: u32) -> anyhow::Result<Vec<u8>> {
316    let tlv = tlv::TlvItemEnc {
317        tag: 0,
318        value: tlv::TlvItemValueEnc::StructInvisible(vec![
319        (0, tlv::TlvItemValueEnc::UInt32(day_entry_id)).into(),
320        ]),
321    };
322    Ok(tlv.encode()?)
323}
324
325// Attribute decoders
326
327/// Decode TariffInfo attribute (0x0000)
328pub fn decode_tariff_info(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<TariffInformation>> {
329    if let tlv::TlvItemValue::List(_fields) = inp {
330        // Struct with fields
331        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
332        Ok(Some(TariffInformation {
333                tariff_label: item.get_string_owned(&[0]),
334                provider_name: item.get_string_owned(&[1]),
335                block_mode: item.get_int(&[3]).and_then(|v| BlockMode::from_u8(v as u8)),
336        }))
337    //} else if let tlv::TlvItemValue::Null = inp {
338    //    // Null value for nullable struct
339    //    Ok(None)
340    } else {
341    Ok(None)
342    //    Err(anyhow::anyhow!("Expected struct fields or null"))
343    }
344}
345
346/// Decode TariffUnit attribute (0x0001)
347pub fn decode_tariff_unit(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
348    if let tlv::TlvItemValue::Int(v) = inp {
349        Ok(Some(*v as u8))
350    } else {
351        Ok(None)
352    }
353}
354
355/// Decode StartDate attribute (0x0002)
356pub fn decode_start_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
357    if let tlv::TlvItemValue::Int(v) = inp {
358        Ok(Some(*v))
359    } else {
360        Ok(None)
361    }
362}
363
364/// Decode DayEntries attribute (0x0003)
365pub fn decode_day_entries(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DayEntry>> {
366    let mut res = Vec::new();
367    if let tlv::TlvItemValue::List(v) = inp {
368        for item in v {
369            res.push(DayEntry {
370                day_entry_id: item.get_int(&[0]).map(|v| v as u32),
371                start_time: item.get_int(&[1]).map(|v| v as u16),
372                duration: item.get_int(&[2]).map(|v| v as u16),
373                randomization_offset: item.get_int(&[3]).map(|v| v as i16),
374                randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
375            });
376        }
377    }
378    Ok(res)
379}
380
381/// Decode DayPatterns attribute (0x0004)
382pub fn decode_day_patterns(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DayPattern>> {
383    let mut res = Vec::new();
384    if let tlv::TlvItemValue::List(v) = inp {
385        for item in v {
386            res.push(DayPattern {
387                day_pattern_id: item.get_int(&[0]).map(|v| v as u32),
388                days_of_week: item.get_int(&[1]).map(|v| v as u8),
389                day_entry_i_ds: {
390                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
391                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
392                        Some(items)
393                    } else {
394                        None
395                    }
396                },
397            });
398        }
399    }
400    Ok(res)
401}
402
403/// Decode CalendarPeriods attribute (0x0005)
404pub fn decode_calendar_periods(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CalendarPeriod>> {
405    let mut res = Vec::new();
406    if let tlv::TlvItemValue::List(v) = inp {
407        for item in v {
408            res.push(CalendarPeriod {
409                start_date: item.get_int(&[0]),
410                day_pattern_i_ds: {
411                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
412                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
413                        Some(items)
414                    } else {
415                        None
416                    }
417                },
418            });
419        }
420    }
421    Ok(res)
422}
423
424/// Decode IndividualDays attribute (0x0006)
425pub fn decode_individual_days(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Day>> {
426    let mut res = Vec::new();
427    if let tlv::TlvItemValue::List(v) = inp {
428        for item in v {
429            res.push(Day {
430                date: item.get_int(&[0]),
431                day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
432                day_entry_i_ds: {
433                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
434                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
435                        Some(items)
436                    } else {
437                        None
438                    }
439                },
440            });
441        }
442    }
443    Ok(res)
444}
445
446/// Decode CurrentDay attribute (0x0007)
447pub fn decode_current_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
448    if let tlv::TlvItemValue::List(_fields) = inp {
449        // Struct with fields
450        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
451        Ok(Some(Day {
452                date: item.get_int(&[0]),
453                day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
454                day_entry_i_ds: {
455                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
456                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
457                        Some(items)
458                    } else {
459                        None
460                    }
461                },
462        }))
463    //} else if let tlv::TlvItemValue::Null = inp {
464    //    // Null value for nullable struct
465    //    Ok(None)
466    } else {
467    Ok(None)
468    //    Err(anyhow::anyhow!("Expected struct fields or null"))
469    }
470}
471
472/// Decode NextDay attribute (0x0008)
473pub fn decode_next_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
474    if let tlv::TlvItemValue::List(_fields) = inp {
475        // Struct with fields
476        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
477        Ok(Some(Day {
478                date: item.get_int(&[0]),
479                day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
480                day_entry_i_ds: {
481                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
482                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
483                        Some(items)
484                    } else {
485                        None
486                    }
487                },
488        }))
489    //} else if let tlv::TlvItemValue::Null = inp {
490    //    // Null value for nullable struct
491    //    Ok(None)
492    } else {
493    Ok(None)
494    //    Err(anyhow::anyhow!("Expected struct fields or null"))
495    }
496}
497
498/// Decode CurrentDayEntry attribute (0x0009)
499pub fn decode_current_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
500    if let tlv::TlvItemValue::List(_fields) = inp {
501        // Struct with fields
502        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
503        Ok(Some(DayEntry {
504                day_entry_id: item.get_int(&[0]).map(|v| v as u32),
505                start_time: item.get_int(&[1]).map(|v| v as u16),
506                duration: item.get_int(&[2]).map(|v| v as u16),
507                randomization_offset: item.get_int(&[3]).map(|v| v as i16),
508                randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
509        }))
510    //} else if let tlv::TlvItemValue::Null = inp {
511    //    // Null value for nullable struct
512    //    Ok(None)
513    } else {
514    Ok(None)
515    //    Err(anyhow::anyhow!("Expected struct fields or null"))
516    }
517}
518
519/// Decode CurrentDayEntryDate attribute (0x000A)
520pub fn decode_current_day_entry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
521    if let tlv::TlvItemValue::Int(v) = inp {
522        Ok(Some(*v))
523    } else {
524        Ok(None)
525    }
526}
527
528/// Decode NextDayEntry attribute (0x000B)
529pub fn decode_next_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
530    if let tlv::TlvItemValue::List(_fields) = inp {
531        // Struct with fields
532        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
533        Ok(Some(DayEntry {
534                day_entry_id: item.get_int(&[0]).map(|v| v as u32),
535                start_time: item.get_int(&[1]).map(|v| v as u16),
536                duration: item.get_int(&[2]).map(|v| v as u16),
537                randomization_offset: item.get_int(&[3]).map(|v| v as i16),
538                randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
539        }))
540    //} else if let tlv::TlvItemValue::Null = inp {
541    //    // Null value for nullable struct
542    //    Ok(None)
543    } else {
544    Ok(None)
545    //    Err(anyhow::anyhow!("Expected struct fields or null"))
546    }
547}
548
549/// Decode NextDayEntryDate attribute (0x000C)
550pub fn decode_next_day_entry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
551    if let tlv::TlvItemValue::Int(v) = inp {
552        Ok(Some(*v))
553    } else {
554        Ok(None)
555    }
556}
557
558/// Decode TariffComponents attribute (0x000D)
559pub fn decode_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
560    let mut res = Vec::new();
561    if let tlv::TlvItemValue::List(v) = inp {
562        for item in v {
563            res.push(TariffComponent {
564                tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
565                price: {
566                    if let Some(nested_tlv) = item.get(&[1]) {
567                        if let tlv::TlvItemValue::List(_) = nested_tlv {
568                            let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
569                            Some(TariffPrice {
570                price_type: nested_item.get_int(&[0]).map(|v| v as u8),
571                price: nested_item.get_int(&[1]).map(|v| v as u8),
572                price_level: nested_item.get_int(&[2]).map(|v| v as i16),
573                            })
574                        } else {
575                            None
576                        }
577                    } else {
578                        None
579                    }
580                },
581                friendly_credit: item.get_bool(&[2]),
582                auxiliary_load: {
583                    if let Some(nested_tlv) = item.get(&[3]) {
584                        if let tlv::TlvItemValue::List(_) = nested_tlv {
585                            let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
586                            Some(AuxiliaryLoadSwitchSettings {
587                number: nested_item.get_int(&[0]).map(|v| v as u8),
588                required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
589                            })
590                        } else {
591                            None
592                        }
593                    } else {
594                        None
595                    }
596                },
597                peak_period: {
598                    if let Some(nested_tlv) = item.get(&[4]) {
599                        if let tlv::TlvItemValue::List(_) = nested_tlv {
600                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
601                            Some(PeakPeriod {
602                severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
603                peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
604                            })
605                        } else {
606                            None
607                        }
608                    } else {
609                        None
610                    }
611                },
612                threshold: item.get_int(&[6]).map(|v| v as i64),
613                label: item.get_string_owned(&[7]),
614                predicted: item.get_bool(&[8]),
615            });
616        }
617    }
618    Ok(res)
619}
620
621/// Decode TariffPeriods attribute (0x000E)
622pub fn decode_tariff_periods(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffPeriod>> {
623    let mut res = Vec::new();
624    if let tlv::TlvItemValue::List(v) = inp {
625        for item in v {
626            res.push(TariffPeriod {
627                label: item.get_string_owned(&[0]),
628                day_entry_i_ds: {
629                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
630                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
631                        Some(items)
632                    } else {
633                        None
634                    }
635                },
636                tariff_component_i_ds: {
637                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
638                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
639                        Some(items)
640                    } else {
641                        None
642                    }
643                },
644            });
645        }
646    }
647    Ok(res)
648}
649
650/// Decode CurrentTariffComponents attribute (0x000F)
651pub fn decode_current_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
652    let mut res = Vec::new();
653    if let tlv::TlvItemValue::List(v) = inp {
654        for item in v {
655            res.push(TariffComponent {
656                tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
657                price: {
658                    if let Some(nested_tlv) = item.get(&[1]) {
659                        if let tlv::TlvItemValue::List(_) = nested_tlv {
660                            let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
661                            Some(TariffPrice {
662                price_type: nested_item.get_int(&[0]).map(|v| v as u8),
663                price: nested_item.get_int(&[1]).map(|v| v as u8),
664                price_level: nested_item.get_int(&[2]).map(|v| v as i16),
665                            })
666                        } else {
667                            None
668                        }
669                    } else {
670                        None
671                    }
672                },
673                friendly_credit: item.get_bool(&[2]),
674                auxiliary_load: {
675                    if let Some(nested_tlv) = item.get(&[3]) {
676                        if let tlv::TlvItemValue::List(_) = nested_tlv {
677                            let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
678                            Some(AuxiliaryLoadSwitchSettings {
679                number: nested_item.get_int(&[0]).map(|v| v as u8),
680                required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
681                            })
682                        } else {
683                            None
684                        }
685                    } else {
686                        None
687                    }
688                },
689                peak_period: {
690                    if let Some(nested_tlv) = item.get(&[4]) {
691                        if let tlv::TlvItemValue::List(_) = nested_tlv {
692                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
693                            Some(PeakPeriod {
694                severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
695                peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
696                            })
697                        } else {
698                            None
699                        }
700                    } else {
701                        None
702                    }
703                },
704                threshold: item.get_int(&[6]).map(|v| v as i64),
705                label: item.get_string_owned(&[7]),
706                predicted: item.get_bool(&[8]),
707            });
708        }
709    }
710    Ok(res)
711}
712
713/// Decode NextTariffComponents attribute (0x0010)
714pub fn decode_next_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
715    let mut res = Vec::new();
716    if let tlv::TlvItemValue::List(v) = inp {
717        for item in v {
718            res.push(TariffComponent {
719                tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
720                price: {
721                    if let Some(nested_tlv) = item.get(&[1]) {
722                        if let tlv::TlvItemValue::List(_) = nested_tlv {
723                            let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
724                            Some(TariffPrice {
725                price_type: nested_item.get_int(&[0]).map(|v| v as u8),
726                price: nested_item.get_int(&[1]).map(|v| v as u8),
727                price_level: nested_item.get_int(&[2]).map(|v| v as i16),
728                            })
729                        } else {
730                            None
731                        }
732                    } else {
733                        None
734                    }
735                },
736                friendly_credit: item.get_bool(&[2]),
737                auxiliary_load: {
738                    if let Some(nested_tlv) = item.get(&[3]) {
739                        if let tlv::TlvItemValue::List(_) = nested_tlv {
740                            let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
741                            Some(AuxiliaryLoadSwitchSettings {
742                number: nested_item.get_int(&[0]).map(|v| v as u8),
743                required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
744                            })
745                        } else {
746                            None
747                        }
748                    } else {
749                        None
750                    }
751                },
752                peak_period: {
753                    if let Some(nested_tlv) = item.get(&[4]) {
754                        if let tlv::TlvItemValue::List(_) = nested_tlv {
755                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
756                            Some(PeakPeriod {
757                severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
758                peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
759                            })
760                        } else {
761                            None
762                        }
763                    } else {
764                        None
765                    }
766                },
767                threshold: item.get_int(&[6]).map(|v| v as i64),
768                label: item.get_string_owned(&[7]),
769                predicted: item.get_bool(&[8]),
770            });
771        }
772    }
773    Ok(res)
774}
775
776/// Decode DefaultRandomizationOffset attribute (0x0011)
777pub fn decode_default_randomization_offset(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
778    if let tlv::TlvItemValue::Int(v) = inp {
779        Ok(Some(*v as i16))
780    } else {
781        Ok(None)
782    }
783}
784
785/// Decode DefaultRandomizationType attribute (0x0012)
786pub fn decode_default_randomization_type(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntryRandomizationType>> {
787    if let tlv::TlvItemValue::Int(v) = inp {
788        Ok(DayEntryRandomizationType::from_u8(*v as u8))
789    } else {
790        Ok(None)
791    }
792}
793
794
795// JSON dispatcher function
796
797/// Decode attribute value and return as JSON string
798///
799/// # Parameters
800/// * `cluster_id` - The cluster identifier
801/// * `attribute_id` - The attribute identifier
802/// * `tlv_value` - The TLV value to decode
803///
804/// # Returns
805/// JSON string representation of the decoded value or error
806pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
807    // Verify this is the correct cluster
808    if cluster_id != 0x0700 {
809        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0700, got {}\"}}", cluster_id);
810    }
811
812    match attribute_id {
813        0x0000 => {
814            match decode_tariff_info(tlv_value) {
815                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
816                Err(e) => format!("{{\"error\": \"{}\"}}", e),
817            }
818        }
819        0x0001 => {
820            match decode_tariff_unit(tlv_value) {
821                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
822                Err(e) => format!("{{\"error\": \"{}\"}}", e),
823            }
824        }
825        0x0002 => {
826            match decode_start_date(tlv_value) {
827                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
828                Err(e) => format!("{{\"error\": \"{}\"}}", e),
829            }
830        }
831        0x0003 => {
832            match decode_day_entries(tlv_value) {
833                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
834                Err(e) => format!("{{\"error\": \"{}\"}}", e),
835            }
836        }
837        0x0004 => {
838            match decode_day_patterns(tlv_value) {
839                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
840                Err(e) => format!("{{\"error\": \"{}\"}}", e),
841            }
842        }
843        0x0005 => {
844            match decode_calendar_periods(tlv_value) {
845                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
846                Err(e) => format!("{{\"error\": \"{}\"}}", e),
847            }
848        }
849        0x0006 => {
850            match decode_individual_days(tlv_value) {
851                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
852                Err(e) => format!("{{\"error\": \"{}\"}}", e),
853            }
854        }
855        0x0007 => {
856            match decode_current_day(tlv_value) {
857                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
858                Err(e) => format!("{{\"error\": \"{}\"}}", e),
859            }
860        }
861        0x0008 => {
862            match decode_next_day(tlv_value) {
863                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
864                Err(e) => format!("{{\"error\": \"{}\"}}", e),
865            }
866        }
867        0x0009 => {
868            match decode_current_day_entry(tlv_value) {
869                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
870                Err(e) => format!("{{\"error\": \"{}\"}}", e),
871            }
872        }
873        0x000A => {
874            match decode_current_day_entry_date(tlv_value) {
875                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
876                Err(e) => format!("{{\"error\": \"{}\"}}", e),
877            }
878        }
879        0x000B => {
880            match decode_next_day_entry(tlv_value) {
881                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
882                Err(e) => format!("{{\"error\": \"{}\"}}", e),
883            }
884        }
885        0x000C => {
886            match decode_next_day_entry_date(tlv_value) {
887                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
888                Err(e) => format!("{{\"error\": \"{}\"}}", e),
889            }
890        }
891        0x000D => {
892            match decode_tariff_components(tlv_value) {
893                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
894                Err(e) => format!("{{\"error\": \"{}\"}}", e),
895            }
896        }
897        0x000E => {
898            match decode_tariff_periods(tlv_value) {
899                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
900                Err(e) => format!("{{\"error\": \"{}\"}}", e),
901            }
902        }
903        0x000F => {
904            match decode_current_tariff_components(tlv_value) {
905                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
906                Err(e) => format!("{{\"error\": \"{}\"}}", e),
907            }
908        }
909        0x0010 => {
910            match decode_next_tariff_components(tlv_value) {
911                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
912                Err(e) => format!("{{\"error\": \"{}\"}}", e),
913            }
914        }
915        0x0011 => {
916            match decode_default_randomization_offset(tlv_value) {
917                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
918                Err(e) => format!("{{\"error\": \"{}\"}}", e),
919            }
920        }
921        0x0012 => {
922            match decode_default_randomization_type(tlv_value) {
923                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
924                Err(e) => format!("{{\"error\": \"{}\"}}", e),
925            }
926        }
927        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
928    }
929}
930
931/// Get list of all attributes supported by this cluster
932///
933/// # Returns
934/// Vector of tuples containing (attribute_id, attribute_name)
935pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
936    vec![
937        (0x0000, "TariffInfo"),
938        (0x0001, "TariffUnit"),
939        (0x0002, "StartDate"),
940        (0x0003, "DayEntries"),
941        (0x0004, "DayPatterns"),
942        (0x0005, "CalendarPeriods"),
943        (0x0006, "IndividualDays"),
944        (0x0007, "CurrentDay"),
945        (0x0008, "NextDay"),
946        (0x0009, "CurrentDayEntry"),
947        (0x000A, "CurrentDayEntryDate"),
948        (0x000B, "NextDayEntry"),
949        (0x000C, "NextDayEntryDate"),
950        (0x000D, "TariffComponents"),
951        (0x000E, "TariffPeriods"),
952        (0x000F, "CurrentTariffComponents"),
953        (0x0010, "NextTariffComponents"),
954        (0x0011, "DefaultRandomizationOffset"),
955        (0x0012, "DefaultRandomizationType"),
956    ]
957}
958
959// Command listing
960
961pub fn get_command_list() -> Vec<(u32, &'static str)> {
962    vec![
963        (0x00, "GetTariffComponent"),
964        (0x01, "GetDayEntry"),
965    ]
966}
967
968pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
969    match cmd_id {
970        0x00 => Some("GetTariffComponent"),
971        0x01 => Some("GetDayEntry"),
972        _ => None,
973    }
974}
975
976pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
977    match cmd_id {
978        0x00 => Some(vec![
979            crate::clusters::codec::CommandField { tag: 0, name: "tariff_component_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
980        ]),
981        0x01 => Some(vec![
982            crate::clusters::codec::CommandField { tag: 0, name: "day_entry_id", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
983        ]),
984        _ => None,
985    }
986}
987
988pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
989    match cmd_id {
990        0x00 => {
991        let tariff_component_id = crate::clusters::codec::json_util::get_u32(args, "tariff_component_id")?;
992        encode_get_tariff_component(tariff_component_id)
993        }
994        0x01 => {
995        let day_entry_id = crate::clusters::codec::json_util::get_u32(args, "day_entry_id")?;
996        encode_get_day_entry(day_entry_id)
997        }
998        _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
999    }
1000}
1001
1002#[derive(Debug, serde::Serialize)]
1003pub struct GetTariffComponentResponse {
1004    pub label: Option<String>,
1005    pub day_entry_i_ds: Option<Vec<u32>>,
1006    pub tariff_component: Option<TariffComponent>,
1007}
1008
1009#[derive(Debug, serde::Serialize)]
1010pub struct GetDayEntryResponse {
1011    pub day_entry: Option<DayEntry>,
1012}
1013
1014// Command response decoders
1015
1016/// Decode GetTariffComponentResponse command response (00)
1017pub fn decode_get_tariff_component_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetTariffComponentResponse> {
1018    if let tlv::TlvItemValue::List(_fields) = inp {
1019        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1020        Ok(GetTariffComponentResponse {
1021                label: item.get_string_owned(&[0]),
1022                day_entry_i_ds: {
1023                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
1024                        let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
1025                        Some(items)
1026                    } else {
1027                        None
1028                    }
1029                },
1030                tariff_component: {
1031                    if let Some(nested_tlv) = item.get(&[2]) {
1032                        if let tlv::TlvItemValue::List(_) = nested_tlv {
1033                            let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
1034                            Some(TariffComponent {
1035                tariff_component_id: nested_item.get_int(&[0]).map(|v| v as u32),
1036                price: {
1037                    if let Some(nested_tlv) = nested_item.get(&[1]) {
1038                        if let tlv::TlvItemValue::List(_) = nested_tlv {
1039                            let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
1040                            Some(TariffPrice {
1041                price_type: nested_item.get_int(&[0]).map(|v| v as u8),
1042                price: nested_item.get_int(&[1]).map(|v| v as u8),
1043                price_level: nested_item.get_int(&[2]).map(|v| v as i16),
1044                            })
1045                        } else {
1046                            None
1047                        }
1048                    } else {
1049                        None
1050                    }
1051                },
1052                friendly_credit: nested_item.get_bool(&[2]),
1053                auxiliary_load: {
1054                    if let Some(nested_tlv) = nested_item.get(&[3]) {
1055                        if let tlv::TlvItemValue::List(_) = nested_tlv {
1056                            let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
1057                            Some(AuxiliaryLoadSwitchSettings {
1058                number: nested_item.get_int(&[0]).map(|v| v as u8),
1059                required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
1060                            })
1061                        } else {
1062                            None
1063                        }
1064                    } else {
1065                        None
1066                    }
1067                },
1068                peak_period: {
1069                    if let Some(nested_tlv) = nested_item.get(&[4]) {
1070                        if let tlv::TlvItemValue::List(_) = nested_tlv {
1071                            let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
1072                            Some(PeakPeriod {
1073                severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
1074                peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
1075                            })
1076                        } else {
1077                            None
1078                        }
1079                    } else {
1080                        None
1081                    }
1082                },
1083                threshold: nested_item.get_int(&[6]).map(|v| v as i64),
1084                label: nested_item.get_string_owned(&[7]),
1085                predicted: nested_item.get_bool(&[8]),
1086                            })
1087                        } else {
1088                            None
1089                        }
1090                    } else {
1091                        None
1092                    }
1093                },
1094        })
1095    } else {
1096        Err(anyhow::anyhow!("Expected struct fields"))
1097    }
1098}
1099
1100/// Decode GetDayEntryResponse command response (01)
1101pub fn decode_get_day_entry_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetDayEntryResponse> {
1102    if let tlv::TlvItemValue::List(_fields) = inp {
1103        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1104        Ok(GetDayEntryResponse {
1105                day_entry: {
1106                    if let Some(nested_tlv) = item.get(&[0]) {
1107                        if let tlv::TlvItemValue::List(_) = nested_tlv {
1108                            let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
1109                            Some(DayEntry {
1110                day_entry_id: nested_item.get_int(&[0]).map(|v| v as u32),
1111                start_time: nested_item.get_int(&[1]).map(|v| v as u16),
1112                duration: nested_item.get_int(&[2]).map(|v| v as u16),
1113                randomization_offset: nested_item.get_int(&[3]).map(|v| v as i16),
1114                randomization_type: nested_item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
1115                            })
1116                        } else {
1117                            None
1118                        }
1119                    } else {
1120                        None
1121                    }
1122                },
1123        })
1124    } else {
1125        Err(anyhow::anyhow!("Expected struct fields"))
1126    }
1127}
1128
1129// Typed facade (invokes + reads)
1130
1131/// Invoke `GetTariffComponent` command on cluster `Commodity Tariff`.
1132pub async fn get_tariff_component(conn: &crate::controller::Connection, endpoint: u16, tariff_component_id: u32) -> anyhow::Result<GetTariffComponentResponse> {
1133    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?;
1134    decode_get_tariff_component_response(&tlv)
1135}
1136
1137/// Invoke `GetDayEntry` command on cluster `Commodity Tariff`.
1138pub async fn get_day_entry(conn: &crate::controller::Connection, endpoint: u16, day_entry_id: u32) -> anyhow::Result<GetDayEntryResponse> {
1139    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?;
1140    decode_get_day_entry_response(&tlv)
1141}
1142
1143/// Read `TariffInfo` attribute from cluster `Commodity Tariff`.
1144pub async fn read_tariff_info(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<TariffInformation>> {
1145    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFINFO).await?;
1146    decode_tariff_info(&tlv)
1147}
1148
1149/// Read `TariffUnit` attribute from cluster `Commodity Tariff`.
1150pub async fn read_tariff_unit(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
1151    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFUNIT).await?;
1152    decode_tariff_unit(&tlv)
1153}
1154
1155/// Read `StartDate` attribute from cluster `Commodity Tariff`.
1156pub async fn read_start_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
1157    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_STARTDATE).await?;
1158    decode_start_date(&tlv)
1159}
1160
1161/// Read `DayEntries` attribute from cluster `Commodity Tariff`.
1162pub async fn read_day_entries(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DayEntry>> {
1163    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DAYENTRIES).await?;
1164    decode_day_entries(&tlv)
1165}
1166
1167/// Read `DayPatterns` attribute from cluster `Commodity Tariff`.
1168pub async fn read_day_patterns(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<DayPattern>> {
1169    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DAYPATTERNS).await?;
1170    decode_day_patterns(&tlv)
1171}
1172
1173/// Read `CalendarPeriods` attribute from cluster `Commodity Tariff`.
1174pub async fn read_calendar_periods(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<CalendarPeriod>> {
1175    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CALENDARPERIODS).await?;
1176    decode_calendar_periods(&tlv)
1177}
1178
1179/// Read `IndividualDays` attribute from cluster `Commodity Tariff`.
1180pub async fn read_individual_days(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<Day>> {
1181    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_INDIVIDUALDAYS).await?;
1182    decode_individual_days(&tlv)
1183}
1184
1185/// Read `CurrentDay` attribute from cluster `Commodity Tariff`.
1186pub async fn read_current_day(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Day>> {
1187    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTDAY).await?;
1188    decode_current_day(&tlv)
1189}
1190
1191/// Read `NextDay` attribute from cluster `Commodity Tariff`.
1192pub async fn read_next_day(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<Day>> {
1193    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTDAY).await?;
1194    decode_next_day(&tlv)
1195}
1196
1197/// Read `CurrentDayEntry` attribute from cluster `Commodity Tariff`.
1198pub async fn read_current_day_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DayEntry>> {
1199    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTDAYENTRY).await?;
1200    decode_current_day_entry(&tlv)
1201}
1202
1203/// Read `CurrentDayEntryDate` attribute from cluster `Commodity Tariff`.
1204pub async fn read_current_day_entry_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
1205    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTDAYENTRYDATE).await?;
1206    decode_current_day_entry_date(&tlv)
1207}
1208
1209/// Read `NextDayEntry` attribute from cluster `Commodity Tariff`.
1210pub async fn read_next_day_entry(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DayEntry>> {
1211    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTDAYENTRY).await?;
1212    decode_next_day_entry(&tlv)
1213}
1214
1215/// Read `NextDayEntryDate` attribute from cluster `Commodity Tariff`.
1216pub async fn read_next_day_entry_date(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
1217    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTDAYENTRYDATE).await?;
1218    decode_next_day_entry_date(&tlv)
1219}
1220
1221/// Read `TariffComponents` attribute from cluster `Commodity Tariff`.
1222pub async fn read_tariff_components(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffComponent>> {
1223    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFCOMPONENTS).await?;
1224    decode_tariff_components(&tlv)
1225}
1226
1227/// Read `TariffPeriods` attribute from cluster `Commodity Tariff`.
1228pub async fn read_tariff_periods(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffPeriod>> {
1229    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_TARIFFPERIODS).await?;
1230    decode_tariff_periods(&tlv)
1231}
1232
1233/// Read `CurrentTariffComponents` attribute from cluster `Commodity Tariff`.
1234pub async fn read_current_tariff_components(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffComponent>> {
1235    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_CURRENTTARIFFCOMPONENTS).await?;
1236    decode_current_tariff_components(&tlv)
1237}
1238
1239/// Read `NextTariffComponents` attribute from cluster `Commodity Tariff`.
1240pub async fn read_next_tariff_components(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<TariffComponent>> {
1241    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_NEXTTARIFFCOMPONENTS).await?;
1242    decode_next_tariff_components(&tlv)
1243}
1244
1245/// Read `DefaultRandomizationOffset` attribute from cluster `Commodity Tariff`.
1246pub async fn read_default_randomization_offset(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<i16>> {
1247    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DEFAULTRANDOMIZATIONOFFSET).await?;
1248    decode_default_randomization_offset(&tlv)
1249}
1250
1251/// Read `DefaultRandomizationType` attribute from cluster `Commodity Tariff`.
1252pub async fn read_default_randomization_type(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<DayEntryRandomizationType>> {
1253    let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_COMMODITY_TARIFF, crate::clusters::defs::CLUSTER_COMMODITY_TARIFF_ATTR_ID_DEFAULTRANDOMIZATIONTYPE).await?;
1254    decode_default_randomization_type(&tlv)
1255}
1256