1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum AuxiliaryLoadSetting {
16 Off = 0,
18 On = 1,
20 None = 2,
22}
23
24impl AuxiliaryLoadSetting {
25 pub fn from_u8(value: u8) -> Option<Self> {
27 match value {
28 0 => Some(AuxiliaryLoadSetting::Off),
29 1 => Some(AuxiliaryLoadSetting::On),
30 2 => Some(AuxiliaryLoadSetting::None),
31 _ => None,
32 }
33 }
34
35 pub fn to_u8(self) -> u8 {
37 self as u8
38 }
39}
40
41impl From<AuxiliaryLoadSetting> for u8 {
42 fn from(val: AuxiliaryLoadSetting) -> Self {
43 val as u8
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48#[repr(u8)]
49pub enum BlockMode {
50 Noblock = 0,
52 Combined = 1,
54 Individual = 2,
56}
57
58impl BlockMode {
59 pub fn from_u8(value: u8) -> Option<Self> {
61 match value {
62 0 => Some(BlockMode::Noblock),
63 1 => Some(BlockMode::Combined),
64 2 => Some(BlockMode::Individual),
65 _ => None,
66 }
67 }
68
69 pub fn to_u8(self) -> u8 {
71 self as u8
72 }
73}
74
75impl From<BlockMode> for u8 {
76 fn from(val: BlockMode) -> Self {
77 val as u8
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
82#[repr(u8)]
83pub enum DayEntryRandomizationType {
84 None = 0,
86 Fixed = 1,
88 Random = 2,
90 Randompositive = 3,
92 Randomnegative = 4,
94}
95
96impl DayEntryRandomizationType {
97 pub fn from_u8(value: u8) -> Option<Self> {
99 match value {
100 0 => Some(DayEntryRandomizationType::None),
101 1 => Some(DayEntryRandomizationType::Fixed),
102 2 => Some(DayEntryRandomizationType::Random),
103 3 => Some(DayEntryRandomizationType::Randompositive),
104 4 => Some(DayEntryRandomizationType::Randomnegative),
105 _ => None,
106 }
107 }
108
109 pub fn to_u8(self) -> u8 {
111 self as u8
112 }
113}
114
115impl From<DayEntryRandomizationType> for u8 {
116 fn from(val: DayEntryRandomizationType) -> Self {
117 val as u8
118 }
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
122#[repr(u8)]
123pub enum DayType {
124 Standard = 0,
126 Holiday = 1,
128 Dynamic = 2,
130 Event = 3,
132}
133
134impl DayType {
135 pub fn from_u8(value: u8) -> Option<Self> {
137 match value {
138 0 => Some(DayType::Standard),
139 1 => Some(DayType::Holiday),
140 2 => Some(DayType::Dynamic),
141 3 => Some(DayType::Event),
142 _ => None,
143 }
144 }
145
146 pub fn to_u8(self) -> u8 {
148 self as u8
149 }
150}
151
152impl From<DayType> for u8 {
153 fn from(val: DayType) -> Self {
154 val as u8
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
159#[repr(u8)]
160pub enum PeakPeriodSeverity {
161 Unused = 0,
163 Low = 1,
165 Medium = 2,
167 High = 3,
169}
170
171impl PeakPeriodSeverity {
172 pub fn from_u8(value: u8) -> Option<Self> {
174 match value {
175 0 => Some(PeakPeriodSeverity::Unused),
176 1 => Some(PeakPeriodSeverity::Low),
177 2 => Some(PeakPeriodSeverity::Medium),
178 3 => Some(PeakPeriodSeverity::High),
179 _ => None,
180 }
181 }
182
183 pub fn to_u8(self) -> u8 {
185 self as u8
186 }
187}
188
189impl From<PeakPeriodSeverity> for u8 {
190 fn from(val: PeakPeriodSeverity) -> Self {
191 val as u8
192 }
193}
194
195pub type DayPatternDayOfWeek = u8;
199
200pub mod daypatterndayofweek {
202 pub const SUNDAY: u8 = 0x01;
204 pub const MONDAY: u8 = 0x02;
206 pub const TUESDAY: u8 = 0x04;
208 pub const WEDNESDAY: u8 = 0x08;
210 pub const THURSDAY: u8 = 0x10;
212 pub const FRIDAY: u8 = 0x20;
214 pub const SATURDAY: u8 = 0x40;
216}
217
218#[derive(Debug, serde::Serialize)]
221pub struct AuxiliaryLoadSwitchSettings {
222 pub number: Option<u8>,
223 pub required_state: Option<AuxiliaryLoadSetting>,
224}
225
226#[derive(Debug, serde::Serialize)]
227pub struct AuxiliaryLoadSwitchesSettings {
228 pub switch_states: Option<Vec<AuxiliaryLoadSwitchSettings>>,
229}
230
231#[derive(Debug, serde::Serialize)]
232pub struct CalendarPeriod {
233 pub start_date: Option<u64>,
234 pub day_pattern_i_ds: Option<Vec<u32>>,
235}
236
237#[derive(Debug, serde::Serialize)]
238pub struct DayEntry {
239 pub day_entry_id: Option<u32>,
240 pub start_time: Option<u16>,
241 pub duration: Option<u16>,
242 pub randomization_offset: Option<i16>,
243 pub randomization_type: Option<DayEntryRandomizationType>,
244}
245
246#[derive(Debug, serde::Serialize)]
247pub struct DayPattern {
248 pub day_pattern_id: Option<u32>,
249 pub days_of_week: Option<DayPatternDayOfWeek>,
250 pub day_entry_i_ds: Option<Vec<u32>>,
251}
252
253#[derive(Debug, serde::Serialize)]
254pub struct Day {
255 pub date: Option<u64>,
256 pub day_type: Option<DayType>,
257 pub day_entry_i_ds: Option<Vec<u32>>,
258}
259
260#[derive(Debug, serde::Serialize)]
261pub struct PeakPeriod {
262 pub severity: Option<PeakPeriodSeverity>,
263 pub peak_period: Option<u16>,
264}
265
266#[derive(Debug, serde::Serialize)]
267pub struct TariffComponent {
268 pub tariff_component_id: Option<u32>,
269 pub price: Option<TariffPrice>,
270 pub friendly_credit: Option<bool>,
271 pub auxiliary_load: Option<AuxiliaryLoadSwitchSettings>,
272 pub peak_period: Option<PeakPeriod>,
273 pub threshold: Option<i64>,
274 pub label: Option<String>,
275 pub predicted: Option<bool>,
276}
277
278#[derive(Debug, serde::Serialize)]
279pub struct TariffInformation {
280 pub tariff_label: Option<String>,
281 pub provider_name: Option<String>,
282 pub block_mode: Option<BlockMode>,
283}
284
285#[derive(Debug, serde::Serialize)]
286pub struct TariffPeriod {
287 pub label: Option<String>,
288 pub day_entry_i_ds: Option<Vec<u32>>,
289 pub tariff_component_i_ds: Option<Vec<u32>>,
290}
291
292#[derive(Debug, serde::Serialize)]
293pub struct TariffPrice {
294 pub price_type: Option<u8>,
295 pub price: Option<u8>,
296 pub price_level: Option<i16>,
297}
298
299pub fn encode_get_tariff_component(tariff_component_id: u32) -> anyhow::Result<Vec<u8>> {
303 let tlv = tlv::TlvItemEnc {
304 tag: 0,
305 value: tlv::TlvItemValueEnc::StructInvisible(vec![
306 (0, tlv::TlvItemValueEnc::UInt32(tariff_component_id)).into(),
307 ]),
308 };
309 Ok(tlv.encode()?)
310}
311
312pub fn encode_get_day_entry(day_entry_id: u32) -> anyhow::Result<Vec<u8>> {
314 let tlv = tlv::TlvItemEnc {
315 tag: 0,
316 value: tlv::TlvItemValueEnc::StructInvisible(vec![
317 (0, tlv::TlvItemValueEnc::UInt32(day_entry_id)).into(),
318 ]),
319 };
320 Ok(tlv.encode()?)
321}
322
323pub fn decode_tariff_info(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<TariffInformation>> {
327 if let tlv::TlvItemValue::List(_fields) = inp {
328 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
330 Ok(Some(TariffInformation {
331 tariff_label: item.get_string_owned(&[0]),
332 provider_name: item.get_string_owned(&[1]),
333 block_mode: item.get_int(&[3]).and_then(|v| BlockMode::from_u8(v as u8)),
334 }))
335 } else {
339 Ok(None)
340 }
342}
343
344pub fn decode_tariff_unit(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
346 if let tlv::TlvItemValue::Int(v) = inp {
347 Ok(Some(*v as u8))
348 } else {
349 Ok(None)
350 }
351}
352
353pub fn decode_start_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
355 if let tlv::TlvItemValue::Int(v) = inp {
356 Ok(Some(*v))
357 } else {
358 Ok(None)
359 }
360}
361
362pub fn decode_day_entries(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DayEntry>> {
364 let mut res = Vec::new();
365 if let tlv::TlvItemValue::List(v) = inp {
366 for item in v {
367 res.push(DayEntry {
368 day_entry_id: item.get_int(&[0]).map(|v| v as u32),
369 start_time: item.get_int(&[1]).map(|v| v as u16),
370 duration: item.get_int(&[2]).map(|v| v as u16),
371 randomization_offset: item.get_int(&[3]).map(|v| v as i16),
372 randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
373 });
374 }
375 }
376 Ok(res)
377}
378
379pub fn decode_day_patterns(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<DayPattern>> {
381 let mut res = Vec::new();
382 if let tlv::TlvItemValue::List(v) = inp {
383 for item in v {
384 res.push(DayPattern {
385 day_pattern_id: item.get_int(&[0]).map(|v| v as u32),
386 days_of_week: item.get_int(&[1]).map(|v| v as u8),
387 day_entry_i_ds: {
388 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
389 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
390 Some(items)
391 } else {
392 None
393 }
394 },
395 });
396 }
397 }
398 Ok(res)
399}
400
401pub fn decode_calendar_periods(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<CalendarPeriod>> {
403 let mut res = Vec::new();
404 if let tlv::TlvItemValue::List(v) = inp {
405 for item in v {
406 res.push(CalendarPeriod {
407 start_date: item.get_int(&[0]),
408 day_pattern_i_ds: {
409 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
410 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
411 Some(items)
412 } else {
413 None
414 }
415 },
416 });
417 }
418 }
419 Ok(res)
420}
421
422pub fn decode_individual_days(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<Day>> {
424 let mut res = Vec::new();
425 if let tlv::TlvItemValue::List(v) = inp {
426 for item in v {
427 res.push(Day {
428 date: item.get_int(&[0]),
429 day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
430 day_entry_i_ds: {
431 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
432 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
433 Some(items)
434 } else {
435 None
436 }
437 },
438 });
439 }
440 }
441 Ok(res)
442}
443
444pub fn decode_current_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
446 if let tlv::TlvItemValue::List(_fields) = inp {
447 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
449 Ok(Some(Day {
450 date: item.get_int(&[0]),
451 day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
452 day_entry_i_ds: {
453 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
454 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
455 Some(items)
456 } else {
457 None
458 }
459 },
460 }))
461 } else {
465 Ok(None)
466 }
468}
469
470pub fn decode_next_day(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Day>> {
472 if let tlv::TlvItemValue::List(_fields) = inp {
473 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
475 Ok(Some(Day {
476 date: item.get_int(&[0]),
477 day_type: item.get_int(&[1]).and_then(|v| DayType::from_u8(v as u8)),
478 day_entry_i_ds: {
479 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
480 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
481 Some(items)
482 } else {
483 None
484 }
485 },
486 }))
487 } else {
491 Ok(None)
492 }
494}
495
496pub fn decode_current_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
498 if let tlv::TlvItemValue::List(_fields) = inp {
499 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
501 Ok(Some(DayEntry {
502 day_entry_id: item.get_int(&[0]).map(|v| v as u32),
503 start_time: item.get_int(&[1]).map(|v| v as u16),
504 duration: item.get_int(&[2]).map(|v| v as u16),
505 randomization_offset: item.get_int(&[3]).map(|v| v as i16),
506 randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
507 }))
508 } else {
512 Ok(None)
513 }
515}
516
517pub fn decode_current_day_entry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
519 if let tlv::TlvItemValue::Int(v) = inp {
520 Ok(Some(*v))
521 } else {
522 Ok(None)
523 }
524}
525
526pub fn decode_next_day_entry(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntry>> {
528 if let tlv::TlvItemValue::List(_fields) = inp {
529 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
531 Ok(Some(DayEntry {
532 day_entry_id: item.get_int(&[0]).map(|v| v as u32),
533 start_time: item.get_int(&[1]).map(|v| v as u16),
534 duration: item.get_int(&[2]).map(|v| v as u16),
535 randomization_offset: item.get_int(&[3]).map(|v| v as i16),
536 randomization_type: item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
537 }))
538 } else {
542 Ok(None)
543 }
545}
546
547pub fn decode_next_day_entry_date(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
549 if let tlv::TlvItemValue::Int(v) = inp {
550 Ok(Some(*v))
551 } else {
552 Ok(None)
553 }
554}
555
556pub fn decode_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
558 let mut res = Vec::new();
559 if let tlv::TlvItemValue::List(v) = inp {
560 for item in v {
561 res.push(TariffComponent {
562 tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
563 price: {
564 if let Some(nested_tlv) = item.get(&[1]) {
565 if let tlv::TlvItemValue::List(_) = nested_tlv {
566 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
567 Some(TariffPrice {
568 price_type: nested_item.get_int(&[0]).map(|v| v as u8),
569 price: nested_item.get_int(&[1]).map(|v| v as u8),
570 price_level: nested_item.get_int(&[2]).map(|v| v as i16),
571 })
572 } else {
573 None
574 }
575 } else {
576 None
577 }
578 },
579 friendly_credit: item.get_bool(&[2]),
580 auxiliary_load: {
581 if let Some(nested_tlv) = item.get(&[3]) {
582 if let tlv::TlvItemValue::List(_) = nested_tlv {
583 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
584 Some(AuxiliaryLoadSwitchSettings {
585 number: nested_item.get_int(&[0]).map(|v| v as u8),
586 required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
587 })
588 } else {
589 None
590 }
591 } else {
592 None
593 }
594 },
595 peak_period: {
596 if let Some(nested_tlv) = item.get(&[4]) {
597 if let tlv::TlvItemValue::List(_) = nested_tlv {
598 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
599 Some(PeakPeriod {
600 severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
601 peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
602 })
603 } else {
604 None
605 }
606 } else {
607 None
608 }
609 },
610 threshold: item.get_int(&[6]).map(|v| v as i64),
611 label: item.get_string_owned(&[7]),
612 predicted: item.get_bool(&[8]),
613 });
614 }
615 }
616 Ok(res)
617}
618
619pub fn decode_tariff_periods(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffPeriod>> {
621 let mut res = Vec::new();
622 if let tlv::TlvItemValue::List(v) = inp {
623 for item in v {
624 res.push(TariffPeriod {
625 label: item.get_string_owned(&[0]),
626 day_entry_i_ds: {
627 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
628 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
629 Some(items)
630 } else {
631 None
632 }
633 },
634 tariff_component_i_ds: {
635 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[2]) {
636 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
637 Some(items)
638 } else {
639 None
640 }
641 },
642 });
643 }
644 }
645 Ok(res)
646}
647
648pub fn decode_current_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
650 let mut res = Vec::new();
651 if let tlv::TlvItemValue::List(v) = inp {
652 for item in v {
653 res.push(TariffComponent {
654 tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
655 price: {
656 if let Some(nested_tlv) = item.get(&[1]) {
657 if let tlv::TlvItemValue::List(_) = nested_tlv {
658 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
659 Some(TariffPrice {
660 price_type: nested_item.get_int(&[0]).map(|v| v as u8),
661 price: nested_item.get_int(&[1]).map(|v| v as u8),
662 price_level: nested_item.get_int(&[2]).map(|v| v as i16),
663 })
664 } else {
665 None
666 }
667 } else {
668 None
669 }
670 },
671 friendly_credit: item.get_bool(&[2]),
672 auxiliary_load: {
673 if let Some(nested_tlv) = item.get(&[3]) {
674 if let tlv::TlvItemValue::List(_) = nested_tlv {
675 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
676 Some(AuxiliaryLoadSwitchSettings {
677 number: nested_item.get_int(&[0]).map(|v| v as u8),
678 required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
679 })
680 } else {
681 None
682 }
683 } else {
684 None
685 }
686 },
687 peak_period: {
688 if let Some(nested_tlv) = item.get(&[4]) {
689 if let tlv::TlvItemValue::List(_) = nested_tlv {
690 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
691 Some(PeakPeriod {
692 severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
693 peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
694 })
695 } else {
696 None
697 }
698 } else {
699 None
700 }
701 },
702 threshold: item.get_int(&[6]).map(|v| v as i64),
703 label: item.get_string_owned(&[7]),
704 predicted: item.get_bool(&[8]),
705 });
706 }
707 }
708 Ok(res)
709}
710
711pub fn decode_next_tariff_components(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<TariffComponent>> {
713 let mut res = Vec::new();
714 if let tlv::TlvItemValue::List(v) = inp {
715 for item in v {
716 res.push(TariffComponent {
717 tariff_component_id: item.get_int(&[0]).map(|v| v as u32),
718 price: {
719 if let Some(nested_tlv) = item.get(&[1]) {
720 if let tlv::TlvItemValue::List(_) = nested_tlv {
721 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
722 Some(TariffPrice {
723 price_type: nested_item.get_int(&[0]).map(|v| v as u8),
724 price: nested_item.get_int(&[1]).map(|v| v as u8),
725 price_level: nested_item.get_int(&[2]).map(|v| v as i16),
726 })
727 } else {
728 None
729 }
730 } else {
731 None
732 }
733 },
734 friendly_credit: item.get_bool(&[2]),
735 auxiliary_load: {
736 if let Some(nested_tlv) = item.get(&[3]) {
737 if let tlv::TlvItemValue::List(_) = nested_tlv {
738 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
739 Some(AuxiliaryLoadSwitchSettings {
740 number: nested_item.get_int(&[0]).map(|v| v as u8),
741 required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
742 })
743 } else {
744 None
745 }
746 } else {
747 None
748 }
749 },
750 peak_period: {
751 if let Some(nested_tlv) = item.get(&[4]) {
752 if let tlv::TlvItemValue::List(_) = nested_tlv {
753 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
754 Some(PeakPeriod {
755 severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
756 peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
757 })
758 } else {
759 None
760 }
761 } else {
762 None
763 }
764 },
765 threshold: item.get_int(&[6]).map(|v| v as i64),
766 label: item.get_string_owned(&[7]),
767 predicted: item.get_bool(&[8]),
768 });
769 }
770 }
771 Ok(res)
772}
773
774pub fn decode_default_randomization_offset(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i16>> {
776 if let tlv::TlvItemValue::Int(v) = inp {
777 Ok(Some(*v as i16))
778 } else {
779 Ok(None)
780 }
781}
782
783pub fn decode_default_randomization_type(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<DayEntryRandomizationType>> {
785 if let tlv::TlvItemValue::Int(v) = inp {
786 Ok(DayEntryRandomizationType::from_u8(*v as u8))
787 } else {
788 Ok(None)
789 }
790}
791
792
793pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
805 if cluster_id != 0x0700 {
807 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0700, got {}\"}}", cluster_id);
808 }
809
810 match attribute_id {
811 0x0000 => {
812 match decode_tariff_info(tlv_value) {
813 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
814 Err(e) => format!("{{\"error\": \"{}\"}}", e),
815 }
816 }
817 0x0001 => {
818 match decode_tariff_unit(tlv_value) {
819 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
820 Err(e) => format!("{{\"error\": \"{}\"}}", e),
821 }
822 }
823 0x0002 => {
824 match decode_start_date(tlv_value) {
825 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
826 Err(e) => format!("{{\"error\": \"{}\"}}", e),
827 }
828 }
829 0x0003 => {
830 match decode_day_entries(tlv_value) {
831 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
832 Err(e) => format!("{{\"error\": \"{}\"}}", e),
833 }
834 }
835 0x0004 => {
836 match decode_day_patterns(tlv_value) {
837 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
838 Err(e) => format!("{{\"error\": \"{}\"}}", e),
839 }
840 }
841 0x0005 => {
842 match decode_calendar_periods(tlv_value) {
843 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
844 Err(e) => format!("{{\"error\": \"{}\"}}", e),
845 }
846 }
847 0x0006 => {
848 match decode_individual_days(tlv_value) {
849 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
850 Err(e) => format!("{{\"error\": \"{}\"}}", e),
851 }
852 }
853 0x0007 => {
854 match decode_current_day(tlv_value) {
855 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
856 Err(e) => format!("{{\"error\": \"{}\"}}", e),
857 }
858 }
859 0x0008 => {
860 match decode_next_day(tlv_value) {
861 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
862 Err(e) => format!("{{\"error\": \"{}\"}}", e),
863 }
864 }
865 0x0009 => {
866 match decode_current_day_entry(tlv_value) {
867 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
868 Err(e) => format!("{{\"error\": \"{}\"}}", e),
869 }
870 }
871 0x000A => {
872 match decode_current_day_entry_date(tlv_value) {
873 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
874 Err(e) => format!("{{\"error\": \"{}\"}}", e),
875 }
876 }
877 0x000B => {
878 match decode_next_day_entry(tlv_value) {
879 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
880 Err(e) => format!("{{\"error\": \"{}\"}}", e),
881 }
882 }
883 0x000C => {
884 match decode_next_day_entry_date(tlv_value) {
885 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
886 Err(e) => format!("{{\"error\": \"{}\"}}", e),
887 }
888 }
889 0x000D => {
890 match decode_tariff_components(tlv_value) {
891 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
892 Err(e) => format!("{{\"error\": \"{}\"}}", e),
893 }
894 }
895 0x000E => {
896 match decode_tariff_periods(tlv_value) {
897 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
898 Err(e) => format!("{{\"error\": \"{}\"}}", e),
899 }
900 }
901 0x000F => {
902 match decode_current_tariff_components(tlv_value) {
903 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
904 Err(e) => format!("{{\"error\": \"{}\"}}", e),
905 }
906 }
907 0x0010 => {
908 match decode_next_tariff_components(tlv_value) {
909 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
910 Err(e) => format!("{{\"error\": \"{}\"}}", e),
911 }
912 }
913 0x0011 => {
914 match decode_default_randomization_offset(tlv_value) {
915 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
916 Err(e) => format!("{{\"error\": \"{}\"}}", e),
917 }
918 }
919 0x0012 => {
920 match decode_default_randomization_type(tlv_value) {
921 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
922 Err(e) => format!("{{\"error\": \"{}\"}}", e),
923 }
924 }
925 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
926 }
927}
928
929pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
934 vec![
935 (0x0000, "TariffInfo"),
936 (0x0001, "TariffUnit"),
937 (0x0002, "StartDate"),
938 (0x0003, "DayEntries"),
939 (0x0004, "DayPatterns"),
940 (0x0005, "CalendarPeriods"),
941 (0x0006, "IndividualDays"),
942 (0x0007, "CurrentDay"),
943 (0x0008, "NextDay"),
944 (0x0009, "CurrentDayEntry"),
945 (0x000A, "CurrentDayEntryDate"),
946 (0x000B, "NextDayEntry"),
947 (0x000C, "NextDayEntryDate"),
948 (0x000D, "TariffComponents"),
949 (0x000E, "TariffPeriods"),
950 (0x000F, "CurrentTariffComponents"),
951 (0x0010, "NextTariffComponents"),
952 (0x0011, "DefaultRandomizationOffset"),
953 (0x0012, "DefaultRandomizationType"),
954 ]
955}
956
957#[derive(Debug, serde::Serialize)]
958pub struct GetTariffComponentResponse {
959 pub label: Option<String>,
960 pub day_entry_i_ds: Option<Vec<u32>>,
961 pub tariff_component: Option<TariffComponent>,
962}
963
964#[derive(Debug, serde::Serialize)]
965pub struct GetDayEntryResponse {
966 pub day_entry: Option<DayEntry>,
967}
968
969pub fn decode_get_tariff_component_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetTariffComponentResponse> {
973 if let tlv::TlvItemValue::List(_fields) = inp {
974 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
975 Ok(GetTariffComponentResponse {
976 label: item.get_string_owned(&[0]),
977 day_entry_i_ds: {
978 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[1]) {
979 let items: Vec<u32> = l.iter().filter_map(|e| { if let tlv::TlvItemValue::Int(v) = &e.value { Some(*v as u32) } else { None } }).collect();
980 Some(items)
981 } else {
982 None
983 }
984 },
985 tariff_component: {
986 if let Some(nested_tlv) = item.get(&[2]) {
987 if let tlv::TlvItemValue::List(_) = nested_tlv {
988 let nested_item = tlv::TlvItem { tag: 2, value: nested_tlv.clone() };
989 Some(TariffComponent {
990 tariff_component_id: nested_item.get_int(&[0]).map(|v| v as u32),
991 price: {
992 if let Some(nested_tlv) = nested_item.get(&[1]) {
993 if let tlv::TlvItemValue::List(_) = nested_tlv {
994 let nested_item = tlv::TlvItem { tag: 1, value: nested_tlv.clone() };
995 Some(TariffPrice {
996 price_type: nested_item.get_int(&[0]).map(|v| v as u8),
997 price: nested_item.get_int(&[1]).map(|v| v as u8),
998 price_level: nested_item.get_int(&[2]).map(|v| v as i16),
999 })
1000 } else {
1001 None
1002 }
1003 } else {
1004 None
1005 }
1006 },
1007 friendly_credit: nested_item.get_bool(&[2]),
1008 auxiliary_load: {
1009 if let Some(nested_tlv) = nested_item.get(&[3]) {
1010 if let tlv::TlvItemValue::List(_) = nested_tlv {
1011 let nested_item = tlv::TlvItem { tag: 3, value: nested_tlv.clone() };
1012 Some(AuxiliaryLoadSwitchSettings {
1013 number: nested_item.get_int(&[0]).map(|v| v as u8),
1014 required_state: nested_item.get_int(&[1]).and_then(|v| AuxiliaryLoadSetting::from_u8(v as u8)),
1015 })
1016 } else {
1017 None
1018 }
1019 } else {
1020 None
1021 }
1022 },
1023 peak_period: {
1024 if let Some(nested_tlv) = nested_item.get(&[4]) {
1025 if let tlv::TlvItemValue::List(_) = nested_tlv {
1026 let nested_item = tlv::TlvItem { tag: 4, value: nested_tlv.clone() };
1027 Some(PeakPeriod {
1028 severity: nested_item.get_int(&[0]).and_then(|v| PeakPeriodSeverity::from_u8(v as u8)),
1029 peak_period: nested_item.get_int(&[1]).map(|v| v as u16),
1030 })
1031 } else {
1032 None
1033 }
1034 } else {
1035 None
1036 }
1037 },
1038 threshold: nested_item.get_int(&[6]).map(|v| v as i64),
1039 label: nested_item.get_string_owned(&[7]),
1040 predicted: nested_item.get_bool(&[8]),
1041 })
1042 } else {
1043 None
1044 }
1045 } else {
1046 None
1047 }
1048 },
1049 })
1050 } else {
1051 Err(anyhow::anyhow!("Expected struct fields"))
1052 }
1053}
1054
1055pub fn decode_get_day_entry_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetDayEntryResponse> {
1057 if let tlv::TlvItemValue::List(_fields) = inp {
1058 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1059 Ok(GetDayEntryResponse {
1060 day_entry: {
1061 if let Some(nested_tlv) = item.get(&[0]) {
1062 if let tlv::TlvItemValue::List(_) = nested_tlv {
1063 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
1064 Some(DayEntry {
1065 day_entry_id: nested_item.get_int(&[0]).map(|v| v as u32),
1066 start_time: nested_item.get_int(&[1]).map(|v| v as u16),
1067 duration: nested_item.get_int(&[2]).map(|v| v as u16),
1068 randomization_offset: nested_item.get_int(&[3]).map(|v| v as i16),
1069 randomization_type: nested_item.get_int(&[4]).and_then(|v| DayEntryRandomizationType::from_u8(v as u8)),
1070 })
1071 } else {
1072 None
1073 }
1074 } else {
1075 None
1076 }
1077 },
1078 })
1079 } else {
1080 Err(anyhow::anyhow!("Expected struct fields"))
1081 }
1082}
1083