1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum AuxiliaryLoadSetting {
18 Off = 0,
20 On = 1,
22 None = 2,
24}
25
26impl AuxiliaryLoadSetting {
27 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 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 Noblock = 0,
54 Combined = 1,
56 Individual = 2,
58}
59
60impl BlockMode {
61 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 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 None = 0,
88 Fixed = 1,
90 Random = 2,
92 Randompositive = 3,
94 Randomnegative = 4,
96}
97
98impl DayEntryRandomizationType {
99 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 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 = 0,
128 Holiday = 1,
130 Dynamic = 2,
132 Event = 3,
134}
135
136impl DayType {
137 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 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 = 0,
165 Low = 1,
167 Medium = 2,
169 High = 3,
171}
172
173impl PeakPeriodSeverity {
174 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 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
197pub type DayPatternDayOfWeek = u8;
201
202pub mod daypatterndayofweek {
204 pub const SUNDAY: u8 = 0x01;
206 pub const MONDAY: u8 = 0x02;
208 pub const TUESDAY: u8 = 0x04;
210 pub const WEDNESDAY: u8 = 0x08;
212 pub const THURSDAY: u8 = 0x10;
214 pub const FRIDAY: u8 = 0x20;
216 pub const SATURDAY: u8 = 0x40;
218}
219
220#[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
301pub 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
314pub 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
325pub fn decode_tariff_info(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<TariffInformation>> {
329 if let tlv::TlvItemValue::List(_fields) = inp {
330 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 {
341 Ok(None)
342 }
344}
345
346pub 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
355pub 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
364pub 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
381pub 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
403pub 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
424pub 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
446pub fn decode_current_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
448 if let tlv::TlvItemValue::List(_fields) = inp {
449 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 {
467 Ok(None)
468 }
470}
471
472pub fn decode_next_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
474 if let tlv::TlvItemValue::List(_fields) = inp {
475 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 {
493 Ok(None)
494 }
496}
497
498pub fn decode_current_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
500 if let tlv::TlvItemValue::List(_fields) = inp {
501 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 {
514 Ok(None)
515 }
517}
518
519pub 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
528pub fn decode_next_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
530 if let tlv::TlvItemValue::List(_fields) = inp {
531 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 {
544 Ok(None)
545 }
547}
548
549pub 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
558pub 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
621pub 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
650pub 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
713pub 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
776pub 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
785pub 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
795pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
807 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
931pub 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
959pub 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
1014pub 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
1100pub 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
1129pub 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
1137pub 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
1143pub 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
1149pub 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
1155pub 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
1161pub 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
1167pub 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
1173pub 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
1179pub 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
1185pub 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
1191pub 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
1197pub 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
1203pub 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
1209pub 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
1215pub 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
1221pub 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
1227pub 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
1233pub 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
1239pub 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
1245pub 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
1251pub 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