1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17#[repr(u8)]
18pub enum EnergyTransferStoppedReason {
19 Evstopped = 0,
21 Evsestopped = 1,
23 Other = 2,
25}
26
27impl EnergyTransferStoppedReason {
28 pub fn from_u8(value: u8) -> Option<Self> {
30 match value {
31 0 => Some(EnergyTransferStoppedReason::Evstopped),
32 1 => Some(EnergyTransferStoppedReason::Evsestopped),
33 2 => Some(EnergyTransferStoppedReason::Other),
34 _ => None,
35 }
36 }
37
38 pub fn to_u8(self) -> u8 {
40 self as u8
41 }
42}
43
44impl From<EnergyTransferStoppedReason> for u8 {
45 fn from(val: EnergyTransferStoppedReason) -> Self {
46 val as u8
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
51#[repr(u8)]
52pub enum FaultState {
53 Noerror = 0,
55 Meterfailure = 1,
57 Overvoltage = 2,
59 Undervoltage = 3,
61 Overcurrent = 4,
63 Contactwetfailure = 5,
65 Contactdryfailure = 6,
67 Groundfault = 7,
69 Powerloss = 8,
71 Powerquality = 9,
73 Pilotshortcircuit = 10,
75 Emergencystop = 11,
77 Evdisconnected = 12,
79 Wrongpowersupply = 13,
81 Liveneutralswap = 14,
83 Overtemperature = 15,
85 Other = 255,
87}
88
89impl FaultState {
90 pub fn from_u8(value: u8) -> Option<Self> {
92 match value {
93 0 => Some(FaultState::Noerror),
94 1 => Some(FaultState::Meterfailure),
95 2 => Some(FaultState::Overvoltage),
96 3 => Some(FaultState::Undervoltage),
97 4 => Some(FaultState::Overcurrent),
98 5 => Some(FaultState::Contactwetfailure),
99 6 => Some(FaultState::Contactdryfailure),
100 7 => Some(FaultState::Groundfault),
101 8 => Some(FaultState::Powerloss),
102 9 => Some(FaultState::Powerquality),
103 10 => Some(FaultState::Pilotshortcircuit),
104 11 => Some(FaultState::Emergencystop),
105 12 => Some(FaultState::Evdisconnected),
106 13 => Some(FaultState::Wrongpowersupply),
107 14 => Some(FaultState::Liveneutralswap),
108 15 => Some(FaultState::Overtemperature),
109 255 => Some(FaultState::Other),
110 _ => None,
111 }
112 }
113
114 pub fn to_u8(self) -> u8 {
116 self as u8
117 }
118}
119
120impl From<FaultState> for u8 {
121 fn from(val: FaultState) -> Self {
122 val as u8
123 }
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
127#[repr(u8)]
128pub enum State {
129 Notpluggedin = 0,
131 Pluggedinnodemand = 1,
133 Pluggedindemand = 2,
135 Pluggedincharging = 3,
137 Pluggedindischarging = 4,
139 Sessionending = 5,
141 Fault = 6,
143}
144
145impl State {
146 pub fn from_u8(value: u8) -> Option<Self> {
148 match value {
149 0 => Some(State::Notpluggedin),
150 1 => Some(State::Pluggedinnodemand),
151 2 => Some(State::Pluggedindemand),
152 3 => Some(State::Pluggedincharging),
153 4 => Some(State::Pluggedindischarging),
154 5 => Some(State::Sessionending),
155 6 => Some(State::Fault),
156 _ => None,
157 }
158 }
159
160 pub fn to_u8(self) -> u8 {
162 self as u8
163 }
164}
165
166impl From<State> for u8 {
167 fn from(val: State) -> Self {
168 val as u8
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
173#[repr(u8)]
174pub enum SupplyState {
175 Disabled = 0,
177 Chargingenabled = 1,
179 Dischargingenabled = 2,
181 Disablederror = 3,
183 Disableddiagnostics = 4,
185 Enabled = 5,
187}
188
189impl SupplyState {
190 pub fn from_u8(value: u8) -> Option<Self> {
192 match value {
193 0 => Some(SupplyState::Disabled),
194 1 => Some(SupplyState::Chargingenabled),
195 2 => Some(SupplyState::Dischargingenabled),
196 3 => Some(SupplyState::Disablederror),
197 4 => Some(SupplyState::Disableddiagnostics),
198 5 => Some(SupplyState::Enabled),
199 _ => None,
200 }
201 }
202
203 pub fn to_u8(self) -> u8 {
205 self as u8
206 }
207}
208
209impl From<SupplyState> for u8 {
210 fn from(val: SupplyState) -> Self {
211 val as u8
212 }
213}
214
215pub type TargetDayOfWeek = u8;
219
220pub mod targetdayofweek {
222 pub const SUNDAY: u8 = 0x01;
224 pub const MONDAY: u8 = 0x02;
226 pub const TUESDAY: u8 = 0x04;
228 pub const WEDNESDAY: u8 = 0x08;
230 pub const THURSDAY: u8 = 0x10;
232 pub const FRIDAY: u8 = 0x20;
234 pub const SATURDAY: u8 = 0x40;
236}
237
238#[derive(Debug, serde::Serialize)]
241pub struct ChargingTargetSchedule {
242 pub day_of_week_for_sequence: Option<TargetDayOfWeek>,
243 pub charging_targets: Option<Vec<ChargingTarget>>,
244}
245
246#[derive(Debug, serde::Serialize)]
247pub struct ChargingTarget {
248 pub target_time_minutes_past_midnight: Option<u16>,
249 pub target_so_c: Option<u8>,
250 pub added_energy: Option<u64>,
251}
252
253pub fn encode_enable_charging(charging_enabled_until: Option<u64>, minimum_charge_current: u8, maximum_charge_current: u8) -> anyhow::Result<Vec<u8>> {
257 let tlv = tlv::TlvItemEnc {
258 tag: 0,
259 value: tlv::TlvItemValueEnc::StructInvisible(vec![
260 (0, tlv::TlvItemValueEnc::UInt64(charging_enabled_until.unwrap_or(0))).into(),
261 (1, tlv::TlvItemValueEnc::UInt8(minimum_charge_current)).into(),
262 (2, tlv::TlvItemValueEnc::UInt8(maximum_charge_current)).into(),
263 ]),
264 };
265 Ok(tlv.encode()?)
266}
267
268pub fn encode_enable_discharging(discharging_enabled_until: Option<u64>, maximum_discharge_current: u8) -> anyhow::Result<Vec<u8>> {
270 let tlv = tlv::TlvItemEnc {
271 tag: 0,
272 value: tlv::TlvItemValueEnc::StructInvisible(vec![
273 (0, tlv::TlvItemValueEnc::UInt64(discharging_enabled_until.unwrap_or(0))).into(),
274 (1, tlv::TlvItemValueEnc::UInt8(maximum_discharge_current)).into(),
275 ]),
276 };
277 Ok(tlv.encode()?)
278}
279
280pub fn encode_set_targets(charging_target_schedules: Vec<ChargingTargetSchedule>) -> anyhow::Result<Vec<u8>> {
282 let tlv = tlv::TlvItemEnc {
283 tag: 0,
284 value: tlv::TlvItemValueEnc::StructInvisible(vec![
285 (0, tlv::TlvItemValueEnc::Array(charging_target_schedules.into_iter().map(|v| {
286 let mut fields = Vec::new();
287 if let Some(x) = v.day_of_week_for_sequence { fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
288 if let Some(listv) = v.charging_targets {
289 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
290 let mut nested_fields = Vec::new();
291 if let Some(x) = inner.target_time_minutes_past_midnight { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
292 if let Some(x) = inner.added_energy { nested_fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
294 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
295 }).collect();
296 fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
297 }
298 (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
299 }).collect())).into(),
300 ]),
301 };
302 Ok(tlv.encode()?)
303}
304
305pub fn decode_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<State>> {
309 if let tlv::TlvItemValue::Int(v) = inp {
310 Ok(State::from_u8(*v as u8))
311 } else {
312 Ok(None)
313 }
314}
315
316pub fn decode_supply_state(inp: &tlv::TlvItemValue) -> anyhow::Result<SupplyState> {
318 if let tlv::TlvItemValue::Int(v) = inp {
319 SupplyState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
320 } else {
321 Err(anyhow::anyhow!("Expected Integer"))
322 }
323}
324
325pub fn decode_fault_state(inp: &tlv::TlvItemValue) -> anyhow::Result<FaultState> {
327 if let tlv::TlvItemValue::Int(v) = inp {
328 FaultState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
329 } else {
330 Err(anyhow::anyhow!("Expected Integer"))
331 }
332}
333
334pub fn decode_charging_enabled_until(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
336 if let tlv::TlvItemValue::Int(v) = inp {
337 Ok(Some(*v))
338 } else {
339 Ok(None)
340 }
341}
342
343pub fn decode_discharging_enabled_until(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
345 if let tlv::TlvItemValue::Int(v) = inp {
346 Ok(Some(*v))
347 } else {
348 Ok(None)
349 }
350}
351
352pub fn decode_circuit_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
354 if let tlv::TlvItemValue::Int(v) = inp {
355 Ok(*v as u8)
356 } else {
357 Err(anyhow::anyhow!("Expected UInt8"))
358 }
359}
360
361pub fn decode_minimum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
363 if let tlv::TlvItemValue::Int(v) = inp {
364 Ok(*v as u8)
365 } else {
366 Err(anyhow::anyhow!("Expected UInt8"))
367 }
368}
369
370pub fn decode_maximum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
372 if let tlv::TlvItemValue::Int(v) = inp {
373 Ok(*v as u8)
374 } else {
375 Err(anyhow::anyhow!("Expected UInt8"))
376 }
377}
378
379pub fn decode_maximum_discharge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
381 if let tlv::TlvItemValue::Int(v) = inp {
382 Ok(*v as u8)
383 } else {
384 Err(anyhow::anyhow!("Expected UInt8"))
385 }
386}
387
388pub fn decode_user_maximum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
390 if let tlv::TlvItemValue::Int(v) = inp {
391 Ok(*v as u8)
392 } else {
393 Err(anyhow::anyhow!("Expected UInt8"))
394 }
395}
396
397pub fn decode_randomization_delay_window(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
399 if let tlv::TlvItemValue::Int(v) = inp {
400 Ok(*v as u32)
401 } else {
402 Err(anyhow::anyhow!("Expected UInt32"))
403 }
404}
405
406pub fn decode_next_charge_start_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
408 if let tlv::TlvItemValue::Int(v) = inp {
409 Ok(Some(*v))
410 } else {
411 Ok(None)
412 }
413}
414
415pub fn decode_next_charge_target_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
417 if let tlv::TlvItemValue::Int(v) = inp {
418 Ok(Some(*v))
419 } else {
420 Ok(None)
421 }
422}
423
424pub fn decode_next_charge_required_energy(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
426 if let tlv::TlvItemValue::Int(v) = inp {
427 Ok(Some(*v))
428 } else {
429 Ok(None)
430 }
431}
432
433pub fn decode_next_charge_target_so_c(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
435 if let tlv::TlvItemValue::Int(v) = inp {
436 Ok(Some(*v as u8))
437 } else {
438 Ok(None)
439 }
440}
441
442pub fn decode_approximate_ev_efficiency(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
444 if let tlv::TlvItemValue::Int(v) = inp {
445 Ok(Some(*v as u16))
446 } else {
447 Ok(None)
448 }
449}
450
451pub fn decode_state_of_charge(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
453 if let tlv::TlvItemValue::Int(v) = inp {
454 Ok(Some(*v as u8))
455 } else {
456 Ok(None)
457 }
458}
459
460pub fn decode_battery_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
462 if let tlv::TlvItemValue::Int(v) = inp {
463 Ok(Some(*v))
464 } else {
465 Ok(None)
466 }
467}
468
469pub fn decode_vehicle_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<String>> {
471 if let tlv::TlvItemValue::String(v) = inp {
472 Ok(Some(v.clone()))
473 } else {
474 Ok(None)
475 }
476}
477
478pub fn decode_session_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
480 if let tlv::TlvItemValue::Int(v) = inp {
481 Ok(Some(*v as u32))
482 } else {
483 Ok(None)
484 }
485}
486
487pub fn decode_session_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
489 if let tlv::TlvItemValue::Int(v) = inp {
490 Ok(Some(*v as u32))
491 } else {
492 Ok(None)
493 }
494}
495
496pub fn decode_session_energy_charged(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
498 if let tlv::TlvItemValue::Int(v) = inp {
499 Ok(Some(*v))
500 } else {
501 Ok(None)
502 }
503}
504
505pub fn decode_session_energy_discharged(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
507 if let tlv::TlvItemValue::Int(v) = inp {
508 Ok(Some(*v))
509 } else {
510 Ok(None)
511 }
512}
513
514
515pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
527 if cluster_id != 0x0099 {
529 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0099, got {}\"}}", cluster_id);
530 }
531
532 match attribute_id {
533 0x0000 => {
534 match decode_state(tlv_value) {
535 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
536 Err(e) => format!("{{\"error\": \"{}\"}}", e),
537 }
538 }
539 0x0001 => {
540 match decode_supply_state(tlv_value) {
541 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
542 Err(e) => format!("{{\"error\": \"{}\"}}", e),
543 }
544 }
545 0x0002 => {
546 match decode_fault_state(tlv_value) {
547 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
548 Err(e) => format!("{{\"error\": \"{}\"}}", e),
549 }
550 }
551 0x0003 => {
552 match decode_charging_enabled_until(tlv_value) {
553 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
554 Err(e) => format!("{{\"error\": \"{}\"}}", e),
555 }
556 }
557 0x0004 => {
558 match decode_discharging_enabled_until(tlv_value) {
559 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
560 Err(e) => format!("{{\"error\": \"{}\"}}", e),
561 }
562 }
563 0x0005 => {
564 match decode_circuit_capacity(tlv_value) {
565 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
566 Err(e) => format!("{{\"error\": \"{}\"}}", e),
567 }
568 }
569 0x0006 => {
570 match decode_minimum_charge_current(tlv_value) {
571 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
572 Err(e) => format!("{{\"error\": \"{}\"}}", e),
573 }
574 }
575 0x0007 => {
576 match decode_maximum_charge_current(tlv_value) {
577 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
578 Err(e) => format!("{{\"error\": \"{}\"}}", e),
579 }
580 }
581 0x0008 => {
582 match decode_maximum_discharge_current(tlv_value) {
583 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
584 Err(e) => format!("{{\"error\": \"{}\"}}", e),
585 }
586 }
587 0x0009 => {
588 match decode_user_maximum_charge_current(tlv_value) {
589 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
590 Err(e) => format!("{{\"error\": \"{}\"}}", e),
591 }
592 }
593 0x000A => {
594 match decode_randomization_delay_window(tlv_value) {
595 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
596 Err(e) => format!("{{\"error\": \"{}\"}}", e),
597 }
598 }
599 0x0023 => {
600 match decode_next_charge_start_time(tlv_value) {
601 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
602 Err(e) => format!("{{\"error\": \"{}\"}}", e),
603 }
604 }
605 0x0024 => {
606 match decode_next_charge_target_time(tlv_value) {
607 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
608 Err(e) => format!("{{\"error\": \"{}\"}}", e),
609 }
610 }
611 0x0025 => {
612 match decode_next_charge_required_energy(tlv_value) {
613 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
614 Err(e) => format!("{{\"error\": \"{}\"}}", e),
615 }
616 }
617 0x0026 => {
618 match decode_next_charge_target_so_c(tlv_value) {
619 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
620 Err(e) => format!("{{\"error\": \"{}\"}}", e),
621 }
622 }
623 0x0027 => {
624 match decode_approximate_ev_efficiency(tlv_value) {
625 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
626 Err(e) => format!("{{\"error\": \"{}\"}}", e),
627 }
628 }
629 0x0030 => {
630 match decode_state_of_charge(tlv_value) {
631 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
632 Err(e) => format!("{{\"error\": \"{}\"}}", e),
633 }
634 }
635 0x0031 => {
636 match decode_battery_capacity(tlv_value) {
637 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
638 Err(e) => format!("{{\"error\": \"{}\"}}", e),
639 }
640 }
641 0x0032 => {
642 match decode_vehicle_id(tlv_value) {
643 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
644 Err(e) => format!("{{\"error\": \"{}\"}}", e),
645 }
646 }
647 0x0040 => {
648 match decode_session_id(tlv_value) {
649 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
650 Err(e) => format!("{{\"error\": \"{}\"}}", e),
651 }
652 }
653 0x0041 => {
654 match decode_session_duration(tlv_value) {
655 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
656 Err(e) => format!("{{\"error\": \"{}\"}}", e),
657 }
658 }
659 0x0042 => {
660 match decode_session_energy_charged(tlv_value) {
661 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
662 Err(e) => format!("{{\"error\": \"{}\"}}", e),
663 }
664 }
665 0x0043 => {
666 match decode_session_energy_discharged(tlv_value) {
667 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
668 Err(e) => format!("{{\"error\": \"{}\"}}", e),
669 }
670 }
671 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
672 }
673}
674
675pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
680 vec![
681 (0x0000, "State"),
682 (0x0001, "SupplyState"),
683 (0x0002, "FaultState"),
684 (0x0003, "ChargingEnabledUntil"),
685 (0x0004, "DischargingEnabledUntil"),
686 (0x0005, "CircuitCapacity"),
687 (0x0006, "MinimumChargeCurrent"),
688 (0x0007, "MaximumChargeCurrent"),
689 (0x0008, "MaximumDischargeCurrent"),
690 (0x0009, "UserMaximumChargeCurrent"),
691 (0x000A, "RandomizationDelayWindow"),
692 (0x0023, "NextChargeStartTime"),
693 (0x0024, "NextChargeTargetTime"),
694 (0x0025, "NextChargeRequiredEnergy"),
695 (0x0026, "NextChargeTargetSoC"),
696 (0x0027, "ApproximateEVEfficiency"),
697 (0x0030, "StateOfCharge"),
698 (0x0031, "BatteryCapacity"),
699 (0x0032, "VehicleID"),
700 (0x0040, "SessionID"),
701 (0x0041, "SessionDuration"),
702 (0x0042, "SessionEnergyCharged"),
703 (0x0043, "SessionEnergyDischarged"),
704 ]
705}
706
707#[derive(Debug, serde::Serialize)]
708pub struct GetTargetsResponse {
709 pub charging_target_schedules: Option<Vec<ChargingTargetSchedule>>,
710}
711
712pub fn decode_get_targets_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetTargetsResponse> {
716 if let tlv::TlvItemValue::List(_fields) = inp {
717 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
718 Ok(GetTargetsResponse {
719 charging_target_schedules: {
720 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
721 let mut items = Vec::new();
722 for list_item in l {
723 items.push(ChargingTargetSchedule {
724 day_of_week_for_sequence: list_item.get_int(&[0]).map(|v| v as u8),
725 charging_targets: {
726 if let Some(tlv::TlvItemValue::List(l)) = list_item.get(&[1]) {
727 let mut items = Vec::new();
728 for list_item in l {
729 items.push(ChargingTarget {
730 target_time_minutes_past_midnight: list_item.get_int(&[0]).map(|v| v as u16),
731 target_so_c: list_item.get_int(&[1]).map(|v| v as u8),
732 added_energy: list_item.get_int(&[2]),
733 });
734 }
735 Some(items)
736 } else {
737 None
738 }
739 },
740 });
741 }
742 Some(items)
743 } else {
744 None
745 }
746 },
747 })
748 } else {
749 Err(anyhow::anyhow!("Expected struct fields"))
750 }
751}
752
753#[derive(Debug, serde::Serialize)]
754pub struct EVConnectedEvent {
755 pub session_id: Option<u32>,
756}
757
758#[derive(Debug, serde::Serialize)]
759pub struct EVNotDetectedEvent {
760 pub session_id: Option<u32>,
761 pub state: Option<State>,
762 pub session_duration: Option<u32>,
763 pub session_energy_charged: Option<u64>,
764 pub session_energy_discharged: Option<u64>,
765}
766
767#[derive(Debug, serde::Serialize)]
768pub struct EnergyTransferStartedEvent {
769 pub session_id: Option<u32>,
770 pub state: Option<State>,
771 pub maximum_current: Option<u8>,
772 pub maximum_discharge_current: Option<u8>,
773}
774
775#[derive(Debug, serde::Serialize)]
776pub struct EnergyTransferStoppedEvent {
777 pub session_id: Option<u32>,
778 pub state: Option<State>,
779 pub reason: Option<EnergyTransferStoppedReason>,
780 pub energy_transferred: Option<u64>,
781 pub energy_discharged: Option<u64>,
782}
783
784#[derive(Debug, serde::Serialize)]
785pub struct FaultEvent {
786 pub session_id: Option<u32>,
787 pub state: Option<State>,
788 pub fault_state_previous_state: Option<FaultState>,
789 pub fault_state_current_state: Option<FaultState>,
790}
791
792#[derive(Debug, serde::Serialize)]
793pub struct RFIDEvent {
794 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
795 pub uid: Option<Vec<u8>>,
796}
797
798pub fn decode_ev_connected_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EVConnectedEvent> {
802 if let tlv::TlvItemValue::List(_fields) = inp {
803 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
804 Ok(EVConnectedEvent {
805 session_id: item.get_int(&[0]).map(|v| v as u32),
806 })
807 } else {
808 Err(anyhow::anyhow!("Expected struct fields"))
809 }
810}
811
812pub fn decode_ev_not_detected_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EVNotDetectedEvent> {
814 if let tlv::TlvItemValue::List(_fields) = inp {
815 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
816 Ok(EVNotDetectedEvent {
817 session_id: item.get_int(&[0]).map(|v| v as u32),
818 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
819 session_duration: item.get_int(&[2]).map(|v| v as u32),
820 session_energy_charged: item.get_int(&[3]),
821 session_energy_discharged: item.get_int(&[4]),
822 })
823 } else {
824 Err(anyhow::anyhow!("Expected struct fields"))
825 }
826}
827
828pub fn decode_energy_transfer_started_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EnergyTransferStartedEvent> {
830 if let tlv::TlvItemValue::List(_fields) = inp {
831 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
832 Ok(EnergyTransferStartedEvent {
833 session_id: item.get_int(&[0]).map(|v| v as u32),
834 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
835 maximum_current: item.get_int(&[2]).map(|v| v as u8),
836 maximum_discharge_current: item.get_int(&[3]).map(|v| v as u8),
837 })
838 } else {
839 Err(anyhow::anyhow!("Expected struct fields"))
840 }
841}
842
843pub fn decode_energy_transfer_stopped_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EnergyTransferStoppedEvent> {
845 if let tlv::TlvItemValue::List(_fields) = inp {
846 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
847 Ok(EnergyTransferStoppedEvent {
848 session_id: item.get_int(&[0]).map(|v| v as u32),
849 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
850 reason: item.get_int(&[2]).and_then(|v| EnergyTransferStoppedReason::from_u8(v as u8)),
851 energy_transferred: item.get_int(&[4]),
852 energy_discharged: item.get_int(&[5]),
853 })
854 } else {
855 Err(anyhow::anyhow!("Expected struct fields"))
856 }
857}
858
859pub fn decode_fault_event(inp: &tlv::TlvItemValue) -> anyhow::Result<FaultEvent> {
861 if let tlv::TlvItemValue::List(_fields) = inp {
862 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
863 Ok(FaultEvent {
864 session_id: item.get_int(&[0]).map(|v| v as u32),
865 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
866 fault_state_previous_state: item.get_int(&[2]).and_then(|v| FaultState::from_u8(v as u8)),
867 fault_state_current_state: item.get_int(&[4]).and_then(|v| FaultState::from_u8(v as u8)),
868 })
869 } else {
870 Err(anyhow::anyhow!("Expected struct fields"))
871 }
872}
873
874pub fn decode_rfid_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RFIDEvent> {
876 if let tlv::TlvItemValue::List(_fields) = inp {
877 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
878 Ok(RFIDEvent {
879 uid: item.get_octet_string_owned(&[0]),
880 })
881 } else {
882 Err(anyhow::anyhow!("Expected struct fields"))
883 }
884}
885