1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13use crate::clusters::helpers::{serialize_opt_bytes_as_hex};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
19#[repr(u8)]
20pub enum EnergyTransferStoppedReason {
21 Evstopped = 0,
23 Evsestopped = 1,
25 Other = 2,
27}
28
29impl EnergyTransferStoppedReason {
30 pub fn from_u8(value: u8) -> Option<Self> {
32 match value {
33 0 => Some(EnergyTransferStoppedReason::Evstopped),
34 1 => Some(EnergyTransferStoppedReason::Evsestopped),
35 2 => Some(EnergyTransferStoppedReason::Other),
36 _ => None,
37 }
38 }
39
40 pub fn to_u8(self) -> u8 {
42 self as u8
43 }
44}
45
46impl From<EnergyTransferStoppedReason> for u8 {
47 fn from(val: EnergyTransferStoppedReason) -> Self {
48 val as u8
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
53#[repr(u8)]
54pub enum FaultState {
55 Noerror = 0,
57 Meterfailure = 1,
59 Overvoltage = 2,
61 Undervoltage = 3,
63 Overcurrent = 4,
65 Contactwetfailure = 5,
67 Contactdryfailure = 6,
69 Groundfault = 7,
71 Powerloss = 8,
73 Powerquality = 9,
75 Pilotshortcircuit = 10,
77 Emergencystop = 11,
79 Evdisconnected = 12,
81 Wrongpowersupply = 13,
83 Liveneutralswap = 14,
85 Overtemperature = 15,
87 Other = 255,
89}
90
91impl FaultState {
92 pub fn from_u8(value: u8) -> Option<Self> {
94 match value {
95 0 => Some(FaultState::Noerror),
96 1 => Some(FaultState::Meterfailure),
97 2 => Some(FaultState::Overvoltage),
98 3 => Some(FaultState::Undervoltage),
99 4 => Some(FaultState::Overcurrent),
100 5 => Some(FaultState::Contactwetfailure),
101 6 => Some(FaultState::Contactdryfailure),
102 7 => Some(FaultState::Groundfault),
103 8 => Some(FaultState::Powerloss),
104 9 => Some(FaultState::Powerquality),
105 10 => Some(FaultState::Pilotshortcircuit),
106 11 => Some(FaultState::Emergencystop),
107 12 => Some(FaultState::Evdisconnected),
108 13 => Some(FaultState::Wrongpowersupply),
109 14 => Some(FaultState::Liveneutralswap),
110 15 => Some(FaultState::Overtemperature),
111 255 => Some(FaultState::Other),
112 _ => None,
113 }
114 }
115
116 pub fn to_u8(self) -> u8 {
118 self as u8
119 }
120}
121
122impl From<FaultState> for u8 {
123 fn from(val: FaultState) -> Self {
124 val as u8
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
129#[repr(u8)]
130pub enum State {
131 Notpluggedin = 0,
133 Pluggedinnodemand = 1,
135 Pluggedindemand = 2,
137 Pluggedincharging = 3,
139 Pluggedindischarging = 4,
141 Sessionending = 5,
143 Fault = 6,
145}
146
147impl State {
148 pub fn from_u8(value: u8) -> Option<Self> {
150 match value {
151 0 => Some(State::Notpluggedin),
152 1 => Some(State::Pluggedinnodemand),
153 2 => Some(State::Pluggedindemand),
154 3 => Some(State::Pluggedincharging),
155 4 => Some(State::Pluggedindischarging),
156 5 => Some(State::Sessionending),
157 6 => Some(State::Fault),
158 _ => None,
159 }
160 }
161
162 pub fn to_u8(self) -> u8 {
164 self as u8
165 }
166}
167
168impl From<State> for u8 {
169 fn from(val: State) -> Self {
170 val as u8
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
175#[repr(u8)]
176pub enum SupplyState {
177 Disabled = 0,
179 Chargingenabled = 1,
181 Dischargingenabled = 2,
183 Disablederror = 3,
185 Disableddiagnostics = 4,
187 Enabled = 5,
189}
190
191impl SupplyState {
192 pub fn from_u8(value: u8) -> Option<Self> {
194 match value {
195 0 => Some(SupplyState::Disabled),
196 1 => Some(SupplyState::Chargingenabled),
197 2 => Some(SupplyState::Dischargingenabled),
198 3 => Some(SupplyState::Disablederror),
199 4 => Some(SupplyState::Disableddiagnostics),
200 5 => Some(SupplyState::Enabled),
201 _ => None,
202 }
203 }
204
205 pub fn to_u8(self) -> u8 {
207 self as u8
208 }
209}
210
211impl From<SupplyState> for u8 {
212 fn from(val: SupplyState) -> Self {
213 val as u8
214 }
215}
216
217pub type TargetDayOfWeek = u8;
221
222pub mod targetdayofweek {
224 pub const SUNDAY: u8 = 0x01;
226 pub const MONDAY: u8 = 0x02;
228 pub const TUESDAY: u8 = 0x04;
230 pub const WEDNESDAY: u8 = 0x08;
232 pub const THURSDAY: u8 = 0x10;
234 pub const FRIDAY: u8 = 0x20;
236 pub const SATURDAY: u8 = 0x40;
238}
239
240#[derive(Debug, serde::Serialize)]
243pub struct ChargingTargetSchedule {
244 pub day_of_week_for_sequence: Option<TargetDayOfWeek>,
245 pub charging_targets: Option<Vec<ChargingTarget>>,
246}
247
248#[derive(Debug, serde::Serialize)]
249pub struct ChargingTarget {
250 pub target_time_minutes_past_midnight: Option<u16>,
251 pub target_so_c: Option<u8>,
252 pub added_energy: Option<u64>,
253}
254
255pub fn encode_enable_charging(charging_enabled_until: Option<u64>, minimum_charge_current: u8, maximum_charge_current: u8) -> anyhow::Result<Vec<u8>> {
259 let tlv = tlv::TlvItemEnc {
260 tag: 0,
261 value: tlv::TlvItemValueEnc::StructInvisible(vec![
262 (0, tlv::TlvItemValueEnc::UInt64(charging_enabled_until.unwrap_or(0))).into(),
263 (1, tlv::TlvItemValueEnc::UInt8(minimum_charge_current)).into(),
264 (2, tlv::TlvItemValueEnc::UInt8(maximum_charge_current)).into(),
265 ]),
266 };
267 Ok(tlv.encode()?)
268}
269
270pub fn encode_enable_discharging(discharging_enabled_until: Option<u64>, maximum_discharge_current: u8) -> anyhow::Result<Vec<u8>> {
272 let tlv = tlv::TlvItemEnc {
273 tag: 0,
274 value: tlv::TlvItemValueEnc::StructInvisible(vec![
275 (0, tlv::TlvItemValueEnc::UInt64(discharging_enabled_until.unwrap_or(0))).into(),
276 (1, tlv::TlvItemValueEnc::UInt8(maximum_discharge_current)).into(),
277 ]),
278 };
279 Ok(tlv.encode()?)
280}
281
282pub fn encode_set_targets(charging_target_schedules: Vec<ChargingTargetSchedule>) -> anyhow::Result<Vec<u8>> {
284 let tlv = tlv::TlvItemEnc {
285 tag: 0,
286 value: tlv::TlvItemValueEnc::StructInvisible(vec![
287 (0, tlv::TlvItemValueEnc::Array(charging_target_schedules.into_iter().map(|v| {
288 let mut fields = Vec::new();
289 if let Some(x) = v.day_of_week_for_sequence { fields.push((0, tlv::TlvItemValueEnc::UInt8(x)).into()); }
290 if let Some(listv) = v.charging_targets {
291 let inner_vec: Vec<_> = listv.into_iter().map(|inner| {
292 let mut nested_fields = Vec::new();
293 if let Some(x) = inner.target_time_minutes_past_midnight { nested_fields.push((0, tlv::TlvItemValueEnc::UInt16(x)).into()); }
294 if let Some(x) = inner.added_energy { nested_fields.push((2, tlv::TlvItemValueEnc::UInt64(x)).into()); }
296 (0, tlv::TlvItemValueEnc::StructAnon(nested_fields)).into()
297 }).collect();
298 fields.push((1, tlv::TlvItemValueEnc::Array(inner_vec)).into());
299 }
300 (0, tlv::TlvItemValueEnc::StructAnon(fields)).into()
301 }).collect())).into(),
302 ]),
303 };
304 Ok(tlv.encode()?)
305}
306
307pub fn decode_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<State>> {
311 if let tlv::TlvItemValue::Int(v) = inp {
312 Ok(State::from_u8(*v as u8))
313 } else {
314 Ok(None)
315 }
316}
317
318pub fn decode_supply_state(inp: &tlv::TlvItemValue) -> anyhow::Result<SupplyState> {
320 if let tlv::TlvItemValue::Int(v) = inp {
321 SupplyState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
322 } else {
323 Err(anyhow::anyhow!("Expected Integer"))
324 }
325}
326
327pub fn decode_fault_state(inp: &tlv::TlvItemValue) -> anyhow::Result<FaultState> {
329 if let tlv::TlvItemValue::Int(v) = inp {
330 FaultState::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
331 } else {
332 Err(anyhow::anyhow!("Expected Integer"))
333 }
334}
335
336pub fn decode_charging_enabled_until(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
338 if let tlv::TlvItemValue::Int(v) = inp {
339 Ok(Some(*v))
340 } else {
341 Ok(None)
342 }
343}
344
345pub fn decode_discharging_enabled_until(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
347 if let tlv::TlvItemValue::Int(v) = inp {
348 Ok(Some(*v))
349 } else {
350 Ok(None)
351 }
352}
353
354pub fn decode_circuit_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
356 if let tlv::TlvItemValue::Int(v) = inp {
357 Ok(*v as u8)
358 } else {
359 Err(anyhow::anyhow!("Expected UInt8"))
360 }
361}
362
363pub fn decode_minimum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
365 if let tlv::TlvItemValue::Int(v) = inp {
366 Ok(*v as u8)
367 } else {
368 Err(anyhow::anyhow!("Expected UInt8"))
369 }
370}
371
372pub fn decode_maximum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
374 if let tlv::TlvItemValue::Int(v) = inp {
375 Ok(*v as u8)
376 } else {
377 Err(anyhow::anyhow!("Expected UInt8"))
378 }
379}
380
381pub fn decode_maximum_discharge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
383 if let tlv::TlvItemValue::Int(v) = inp {
384 Ok(*v as u8)
385 } else {
386 Err(anyhow::anyhow!("Expected UInt8"))
387 }
388}
389
390pub fn decode_user_maximum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
392 if let tlv::TlvItemValue::Int(v) = inp {
393 Ok(*v as u8)
394 } else {
395 Err(anyhow::anyhow!("Expected UInt8"))
396 }
397}
398
399pub fn decode_randomization_delay_window(inp: &tlv::TlvItemValue) -> anyhow::Result<u32> {
401 if let tlv::TlvItemValue::Int(v) = inp {
402 Ok(*v as u32)
403 } else {
404 Err(anyhow::anyhow!("Expected UInt32"))
405 }
406}
407
408pub fn decode_next_charge_start_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
410 if let tlv::TlvItemValue::Int(v) = inp {
411 Ok(Some(*v))
412 } else {
413 Ok(None)
414 }
415}
416
417pub fn decode_next_charge_target_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
419 if let tlv::TlvItemValue::Int(v) = inp {
420 Ok(Some(*v))
421 } else {
422 Ok(None)
423 }
424}
425
426pub fn decode_next_charge_required_energy(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
428 if let tlv::TlvItemValue::Int(v) = inp {
429 Ok(Some(*v))
430 } else {
431 Ok(None)
432 }
433}
434
435pub fn decode_next_charge_target_so_c(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
437 if let tlv::TlvItemValue::Int(v) = inp {
438 Ok(Some(*v as u8))
439 } else {
440 Ok(None)
441 }
442}
443
444pub fn decode_approximate_ev_efficiency(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
446 if let tlv::TlvItemValue::Int(v) = inp {
447 Ok(Some(*v as u16))
448 } else {
449 Ok(None)
450 }
451}
452
453pub fn decode_state_of_charge(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
455 if let tlv::TlvItemValue::Int(v) = inp {
456 Ok(Some(*v as u8))
457 } else {
458 Ok(None)
459 }
460}
461
462pub fn decode_battery_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
464 if let tlv::TlvItemValue::Int(v) = inp {
465 Ok(Some(*v))
466 } else {
467 Ok(None)
468 }
469}
470
471pub fn decode_vehicle_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<String>> {
473 if let tlv::TlvItemValue::String(v) = inp {
474 Ok(Some(v.clone()))
475 } else {
476 Ok(None)
477 }
478}
479
480pub fn decode_session_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
482 if let tlv::TlvItemValue::Int(v) = inp {
483 Ok(Some(*v as u32))
484 } else {
485 Ok(None)
486 }
487}
488
489pub fn decode_session_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
491 if let tlv::TlvItemValue::Int(v) = inp {
492 Ok(Some(*v as u32))
493 } else {
494 Ok(None)
495 }
496}
497
498pub fn decode_session_energy_charged(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
500 if let tlv::TlvItemValue::Int(v) = inp {
501 Ok(Some(*v))
502 } else {
503 Ok(None)
504 }
505}
506
507pub fn decode_session_energy_discharged(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
509 if let tlv::TlvItemValue::Int(v) = inp {
510 Ok(Some(*v))
511 } else {
512 Ok(None)
513 }
514}
515
516
517pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
529 if cluster_id != 0x0099 {
531 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0099, got {}\"}}", cluster_id);
532 }
533
534 match attribute_id {
535 0x0000 => {
536 match decode_state(tlv_value) {
537 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
538 Err(e) => format!("{{\"error\": \"{}\"}}", e),
539 }
540 }
541 0x0001 => {
542 match decode_supply_state(tlv_value) {
543 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
544 Err(e) => format!("{{\"error\": \"{}\"}}", e),
545 }
546 }
547 0x0002 => {
548 match decode_fault_state(tlv_value) {
549 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
550 Err(e) => format!("{{\"error\": \"{}\"}}", e),
551 }
552 }
553 0x0003 => {
554 match decode_charging_enabled_until(tlv_value) {
555 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
556 Err(e) => format!("{{\"error\": \"{}\"}}", e),
557 }
558 }
559 0x0004 => {
560 match decode_discharging_enabled_until(tlv_value) {
561 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
562 Err(e) => format!("{{\"error\": \"{}\"}}", e),
563 }
564 }
565 0x0005 => {
566 match decode_circuit_capacity(tlv_value) {
567 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
568 Err(e) => format!("{{\"error\": \"{}\"}}", e),
569 }
570 }
571 0x0006 => {
572 match decode_minimum_charge_current(tlv_value) {
573 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
574 Err(e) => format!("{{\"error\": \"{}\"}}", e),
575 }
576 }
577 0x0007 => {
578 match decode_maximum_charge_current(tlv_value) {
579 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
580 Err(e) => format!("{{\"error\": \"{}\"}}", e),
581 }
582 }
583 0x0008 => {
584 match decode_maximum_discharge_current(tlv_value) {
585 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
586 Err(e) => format!("{{\"error\": \"{}\"}}", e),
587 }
588 }
589 0x0009 => {
590 match decode_user_maximum_charge_current(tlv_value) {
591 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
592 Err(e) => format!("{{\"error\": \"{}\"}}", e),
593 }
594 }
595 0x000A => {
596 match decode_randomization_delay_window(tlv_value) {
597 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
598 Err(e) => format!("{{\"error\": \"{}\"}}", e),
599 }
600 }
601 0x0023 => {
602 match decode_next_charge_start_time(tlv_value) {
603 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
604 Err(e) => format!("{{\"error\": \"{}\"}}", e),
605 }
606 }
607 0x0024 => {
608 match decode_next_charge_target_time(tlv_value) {
609 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
610 Err(e) => format!("{{\"error\": \"{}\"}}", e),
611 }
612 }
613 0x0025 => {
614 match decode_next_charge_required_energy(tlv_value) {
615 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
616 Err(e) => format!("{{\"error\": \"{}\"}}", e),
617 }
618 }
619 0x0026 => {
620 match decode_next_charge_target_so_c(tlv_value) {
621 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
622 Err(e) => format!("{{\"error\": \"{}\"}}", e),
623 }
624 }
625 0x0027 => {
626 match decode_approximate_ev_efficiency(tlv_value) {
627 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
628 Err(e) => format!("{{\"error\": \"{}\"}}", e),
629 }
630 }
631 0x0030 => {
632 match decode_state_of_charge(tlv_value) {
633 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
634 Err(e) => format!("{{\"error\": \"{}\"}}", e),
635 }
636 }
637 0x0031 => {
638 match decode_battery_capacity(tlv_value) {
639 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
640 Err(e) => format!("{{\"error\": \"{}\"}}", e),
641 }
642 }
643 0x0032 => {
644 match decode_vehicle_id(tlv_value) {
645 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
646 Err(e) => format!("{{\"error\": \"{}\"}}", e),
647 }
648 }
649 0x0040 => {
650 match decode_session_id(tlv_value) {
651 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
652 Err(e) => format!("{{\"error\": \"{}\"}}", e),
653 }
654 }
655 0x0041 => {
656 match decode_session_duration(tlv_value) {
657 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
658 Err(e) => format!("{{\"error\": \"{}\"}}", e),
659 }
660 }
661 0x0042 => {
662 match decode_session_energy_charged(tlv_value) {
663 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
664 Err(e) => format!("{{\"error\": \"{}\"}}", e),
665 }
666 }
667 0x0043 => {
668 match decode_session_energy_discharged(tlv_value) {
669 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
670 Err(e) => format!("{{\"error\": \"{}\"}}", e),
671 }
672 }
673 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
674 }
675}
676
677pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
682 vec![
683 (0x0000, "State"),
684 (0x0001, "SupplyState"),
685 (0x0002, "FaultState"),
686 (0x0003, "ChargingEnabledUntil"),
687 (0x0004, "DischargingEnabledUntil"),
688 (0x0005, "CircuitCapacity"),
689 (0x0006, "MinimumChargeCurrent"),
690 (0x0007, "MaximumChargeCurrent"),
691 (0x0008, "MaximumDischargeCurrent"),
692 (0x0009, "UserMaximumChargeCurrent"),
693 (0x000A, "RandomizationDelayWindow"),
694 (0x0023, "NextChargeStartTime"),
695 (0x0024, "NextChargeTargetTime"),
696 (0x0025, "NextChargeRequiredEnergy"),
697 (0x0026, "NextChargeTargetSoC"),
698 (0x0027, "ApproximateEVEfficiency"),
699 (0x0030, "StateOfCharge"),
700 (0x0031, "BatteryCapacity"),
701 (0x0032, "VehicleID"),
702 (0x0040, "SessionID"),
703 (0x0041, "SessionDuration"),
704 (0x0042, "SessionEnergyCharged"),
705 (0x0043, "SessionEnergyDischarged"),
706 ]
707}
708
709pub fn get_command_list() -> Vec<(u32, &'static str)> {
712 vec![
713 (0x01, "Disable"),
714 (0x02, "EnableCharging"),
715 (0x03, "EnableDischarging"),
716 (0x04, "StartDiagnostics"),
717 (0x05, "SetTargets"),
718 (0x06, "GetTargets"),
719 (0x07, "ClearTargets"),
720 ]
721}
722
723pub fn get_command_name(cmd_id: u32) -> Option<&'static str> {
724 match cmd_id {
725 0x01 => Some("Disable"),
726 0x02 => Some("EnableCharging"),
727 0x03 => Some("EnableDischarging"),
728 0x04 => Some("StartDiagnostics"),
729 0x05 => Some("SetTargets"),
730 0x06 => Some("GetTargets"),
731 0x07 => Some("ClearTargets"),
732 _ => None,
733 }
734}
735
736pub fn get_command_schema(cmd_id: u32) -> Option<Vec<crate::clusters::codec::CommandField>> {
737 match cmd_id {
738 0x01 => Some(vec![]),
739 0x02 => Some(vec![
740 crate::clusters::codec::CommandField { tag: 0, name: "charging_enabled_until", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: true },
741 crate::clusters::codec::CommandField { tag: 1, name: "minimum_charge_current", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
742 crate::clusters::codec::CommandField { tag: 2, name: "maximum_charge_current", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
743 ]),
744 0x03 => Some(vec![
745 crate::clusters::codec::CommandField { tag: 0, name: "discharging_enabled_until", kind: crate::clusters::codec::FieldKind::U64, optional: false, nullable: true },
746 crate::clusters::codec::CommandField { tag: 1, name: "maximum_discharge_current", kind: crate::clusters::codec::FieldKind::U32, optional: false, nullable: false },
747 ]),
748 0x04 => Some(vec![]),
749 0x05 => Some(vec![
750 crate::clusters::codec::CommandField { tag: 0, name: "charging_target_schedules", kind: crate::clusters::codec::FieldKind::List { entry_type: "ChargingTargetScheduleStruct" }, optional: false, nullable: false },
751 ]),
752 0x06 => Some(vec![]),
753 0x07 => Some(vec![]),
754 _ => None,
755 }
756}
757
758pub fn encode_command_json(cmd_id: u32, args: &serde_json::Value) -> anyhow::Result<Vec<u8>> {
759 match cmd_id {
760 0x01 => Ok(vec![]),
761 0x02 => {
762 let charging_enabled_until = crate::clusters::codec::json_util::get_opt_u64(args, "charging_enabled_until")?;
763 let minimum_charge_current = crate::clusters::codec::json_util::get_u8(args, "minimum_charge_current")?;
764 let maximum_charge_current = crate::clusters::codec::json_util::get_u8(args, "maximum_charge_current")?;
765 encode_enable_charging(charging_enabled_until, minimum_charge_current, maximum_charge_current)
766 }
767 0x03 => {
768 let discharging_enabled_until = crate::clusters::codec::json_util::get_opt_u64(args, "discharging_enabled_until")?;
769 let maximum_discharge_current = crate::clusters::codec::json_util::get_u8(args, "maximum_discharge_current")?;
770 encode_enable_discharging(discharging_enabled_until, maximum_discharge_current)
771 }
772 0x04 => Ok(vec![]),
773 0x05 => Err(anyhow::anyhow!("command \"SetTargets\" has complex args: use raw mode")),
774 0x06 => Ok(vec![]),
775 0x07 => Ok(vec![]),
776 _ => Err(anyhow::anyhow!("unknown command ID: 0x{:02X}", cmd_id)),
777 }
778}
779
780#[derive(Debug, serde::Serialize)]
781pub struct GetTargetsResponse {
782 pub charging_target_schedules: Option<Vec<ChargingTargetSchedule>>,
783}
784
785pub fn decode_get_targets_response(inp: &tlv::TlvItemValue) -> anyhow::Result<GetTargetsResponse> {
789 if let tlv::TlvItemValue::List(_fields) = inp {
790 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
791 Ok(GetTargetsResponse {
792 charging_target_schedules: {
793 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
794 let mut items = Vec::new();
795 for list_item in l {
796 items.push(ChargingTargetSchedule {
797 day_of_week_for_sequence: list_item.get_int(&[0]).map(|v| v as u8),
798 charging_targets: {
799 if let Some(tlv::TlvItemValue::List(l)) = list_item.get(&[1]) {
800 let mut items = Vec::new();
801 for list_item in l {
802 items.push(ChargingTarget {
803 target_time_minutes_past_midnight: list_item.get_int(&[0]).map(|v| v as u16),
804 target_so_c: list_item.get_int(&[1]).map(|v| v as u8),
805 added_energy: list_item.get_int(&[2]),
806 });
807 }
808 Some(items)
809 } else {
810 None
811 }
812 },
813 });
814 }
815 Some(items)
816 } else {
817 None
818 }
819 },
820 })
821 } else {
822 Err(anyhow::anyhow!("Expected struct fields"))
823 }
824}
825
826pub async fn disable(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
830 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_DISABLE, &[]).await?;
831 Ok(())
832}
833
834pub async fn enable_charging(conn: &crate::controller::Connection, endpoint: u16, charging_enabled_until: Option<u64>, minimum_charge_current: u8, maximum_charge_current: u8) -> anyhow::Result<()> {
836 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_ENABLECHARGING, &encode_enable_charging(charging_enabled_until, minimum_charge_current, maximum_charge_current)?).await?;
837 Ok(())
838}
839
840pub async fn enable_discharging(conn: &crate::controller::Connection, endpoint: u16, discharging_enabled_until: Option<u64>, maximum_discharge_current: u8) -> anyhow::Result<()> {
842 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_ENABLEDISCHARGING, &encode_enable_discharging(discharging_enabled_until, maximum_discharge_current)?).await?;
843 Ok(())
844}
845
846pub async fn start_diagnostics(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
848 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_STARTDIAGNOSTICS, &[]).await?;
849 Ok(())
850}
851
852pub async fn set_targets(conn: &crate::controller::Connection, endpoint: u16, charging_target_schedules: Vec<ChargingTargetSchedule>) -> anyhow::Result<()> {
854 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_SETTARGETS, &encode_set_targets(charging_target_schedules)?).await?;
855 Ok(())
856}
857
858pub async fn get_targets(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<GetTargetsResponse> {
860 let tlv = conn.invoke_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_GETTARGETS, &[]).await?;
861 decode_get_targets_response(&tlv)
862}
863
864pub async fn clear_targets(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<()> {
866 conn.invoke_request(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_CMD_ID_CLEARTARGETS, &[]).await?;
867 Ok(())
868}
869
870pub async fn read_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<State>> {
872 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_STATE).await?;
873 decode_state(&tlv)
874}
875
876pub async fn read_supply_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<SupplyState> {
878 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_SUPPLYSTATE).await?;
879 decode_supply_state(&tlv)
880}
881
882pub async fn read_fault_state(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<FaultState> {
884 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_FAULTSTATE).await?;
885 decode_fault_state(&tlv)
886}
887
888pub async fn read_charging_enabled_until(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
890 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_CHARGINGENABLEDUNTIL).await?;
891 decode_charging_enabled_until(&tlv)
892}
893
894pub async fn read_discharging_enabled_until(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
896 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_DISCHARGINGENABLEDUNTIL).await?;
897 decode_discharging_enabled_until(&tlv)
898}
899
900pub async fn read_circuit_capacity(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
902 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_CIRCUITCAPACITY).await?;
903 decode_circuit_capacity(&tlv)
904}
905
906pub async fn read_minimum_charge_current(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
908 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_MINIMUMCHARGECURRENT).await?;
909 decode_minimum_charge_current(&tlv)
910}
911
912pub async fn read_maximum_charge_current(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
914 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_MAXIMUMCHARGECURRENT).await?;
915 decode_maximum_charge_current(&tlv)
916}
917
918pub async fn read_maximum_discharge_current(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
920 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_MAXIMUMDISCHARGECURRENT).await?;
921 decode_maximum_discharge_current(&tlv)
922}
923
924pub async fn read_user_maximum_charge_current(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u8> {
926 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_USERMAXIMUMCHARGECURRENT).await?;
927 decode_user_maximum_charge_current(&tlv)
928}
929
930pub async fn read_randomization_delay_window(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<u32> {
932 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_RANDOMIZATIONDELAYWINDOW).await?;
933 decode_randomization_delay_window(&tlv)
934}
935
936pub async fn read_next_charge_start_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
938 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_NEXTCHARGESTARTTIME).await?;
939 decode_next_charge_start_time(&tlv)
940}
941
942pub async fn read_next_charge_target_time(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
944 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_NEXTCHARGETARGETTIME).await?;
945 decode_next_charge_target_time(&tlv)
946}
947
948pub async fn read_next_charge_required_energy(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
950 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_NEXTCHARGEREQUIREDENERGY).await?;
951 decode_next_charge_required_energy(&tlv)
952}
953
954pub async fn read_next_charge_target_so_c(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
956 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_NEXTCHARGETARGETSOC).await?;
957 decode_next_charge_target_so_c(&tlv)
958}
959
960pub async fn read_approximate_ev_efficiency(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u16>> {
962 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_APPROXIMATEEVEFFICIENCY).await?;
963 decode_approximate_ev_efficiency(&tlv)
964}
965
966pub async fn read_state_of_charge(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u8>> {
968 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_STATEOFCHARGE).await?;
969 decode_state_of_charge(&tlv)
970}
971
972pub async fn read_battery_capacity(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
974 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_BATTERYCAPACITY).await?;
975 decode_battery_capacity(&tlv)
976}
977
978pub async fn read_vehicle_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<String>> {
980 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_VEHICLEID).await?;
981 decode_vehicle_id(&tlv)
982}
983
984pub async fn read_session_id(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
986 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_SESSIONID).await?;
987 decode_session_id(&tlv)
988}
989
990pub async fn read_session_duration(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u32>> {
992 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_SESSIONDURATION).await?;
993 decode_session_duration(&tlv)
994}
995
996pub async fn read_session_energy_charged(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
998 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_SESSIONENERGYCHARGED).await?;
999 decode_session_energy_charged(&tlv)
1000}
1001
1002pub async fn read_session_energy_discharged(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<u64>> {
1004 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ENERGY_EVSE, crate::clusters::defs::CLUSTER_ENERGY_EVSE_ATTR_ID_SESSIONENERGYDISCHARGED).await?;
1005 decode_session_energy_discharged(&tlv)
1006}
1007
1008#[derive(Debug, serde::Serialize)]
1009pub struct EVConnectedEvent {
1010 pub session_id: Option<u32>,
1011}
1012
1013#[derive(Debug, serde::Serialize)]
1014pub struct EVNotDetectedEvent {
1015 pub session_id: Option<u32>,
1016 pub state: Option<State>,
1017 pub session_duration: Option<u32>,
1018 pub session_energy_charged: Option<u64>,
1019 pub session_energy_discharged: Option<u64>,
1020}
1021
1022#[derive(Debug, serde::Serialize)]
1023pub struct EnergyTransferStartedEvent {
1024 pub session_id: Option<u32>,
1025 pub state: Option<State>,
1026 pub maximum_current: Option<u8>,
1027 pub maximum_discharge_current: Option<u8>,
1028}
1029
1030#[derive(Debug, serde::Serialize)]
1031pub struct EnergyTransferStoppedEvent {
1032 pub session_id: Option<u32>,
1033 pub state: Option<State>,
1034 pub reason: Option<EnergyTransferStoppedReason>,
1035 pub energy_transferred: Option<u64>,
1036 pub energy_discharged: Option<u64>,
1037}
1038
1039#[derive(Debug, serde::Serialize)]
1040pub struct FaultEvent {
1041 pub session_id: Option<u32>,
1042 pub state: Option<State>,
1043 pub fault_state_previous_state: Option<FaultState>,
1044 pub fault_state_current_state: Option<FaultState>,
1045}
1046
1047#[derive(Debug, serde::Serialize)]
1048pub struct RFIDEvent {
1049 #[serde(serialize_with = "serialize_opt_bytes_as_hex")]
1050 pub uid: Option<Vec<u8>>,
1051}
1052
1053pub fn decode_ev_connected_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EVConnectedEvent> {
1057 if let tlv::TlvItemValue::List(_fields) = inp {
1058 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1059 Ok(EVConnectedEvent {
1060 session_id: item.get_int(&[0]).map(|v| v as u32),
1061 })
1062 } else {
1063 Err(anyhow::anyhow!("Expected struct fields"))
1064 }
1065}
1066
1067pub fn decode_ev_not_detected_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EVNotDetectedEvent> {
1069 if let tlv::TlvItemValue::List(_fields) = inp {
1070 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1071 Ok(EVNotDetectedEvent {
1072 session_id: item.get_int(&[0]).map(|v| v as u32),
1073 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
1074 session_duration: item.get_int(&[2]).map(|v| v as u32),
1075 session_energy_charged: item.get_int(&[3]),
1076 session_energy_discharged: item.get_int(&[4]),
1077 })
1078 } else {
1079 Err(anyhow::anyhow!("Expected struct fields"))
1080 }
1081}
1082
1083pub fn decode_energy_transfer_started_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EnergyTransferStartedEvent> {
1085 if let tlv::TlvItemValue::List(_fields) = inp {
1086 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1087 Ok(EnergyTransferStartedEvent {
1088 session_id: item.get_int(&[0]).map(|v| v as u32),
1089 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
1090 maximum_current: item.get_int(&[2]).map(|v| v as u8),
1091 maximum_discharge_current: item.get_int(&[3]).map(|v| v as u8),
1092 })
1093 } else {
1094 Err(anyhow::anyhow!("Expected struct fields"))
1095 }
1096}
1097
1098pub fn decode_energy_transfer_stopped_event(inp: &tlv::TlvItemValue) -> anyhow::Result<EnergyTransferStoppedEvent> {
1100 if let tlv::TlvItemValue::List(_fields) = inp {
1101 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1102 Ok(EnergyTransferStoppedEvent {
1103 session_id: item.get_int(&[0]).map(|v| v as u32),
1104 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
1105 reason: item.get_int(&[2]).and_then(|v| EnergyTransferStoppedReason::from_u8(v as u8)),
1106 energy_transferred: item.get_int(&[4]),
1107 energy_discharged: item.get_int(&[5]),
1108 })
1109 } else {
1110 Err(anyhow::anyhow!("Expected struct fields"))
1111 }
1112}
1113
1114pub fn decode_fault_event(inp: &tlv::TlvItemValue) -> anyhow::Result<FaultEvent> {
1116 if let tlv::TlvItemValue::List(_fields) = inp {
1117 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1118 Ok(FaultEvent {
1119 session_id: item.get_int(&[0]).map(|v| v as u32),
1120 state: item.get_int(&[1]).and_then(|v| State::from_u8(v as u8)),
1121 fault_state_previous_state: item.get_int(&[2]).and_then(|v| FaultState::from_u8(v as u8)),
1122 fault_state_current_state: item.get_int(&[4]).and_then(|v| FaultState::from_u8(v as u8)),
1123 })
1124 } else {
1125 Err(anyhow::anyhow!("Expected struct fields"))
1126 }
1127}
1128
1129pub fn decode_rfid_event(inp: &tlv::TlvItemValue) -> anyhow::Result<RFIDEvent> {
1131 if let tlv::TlvItemValue::List(_fields) = inp {
1132 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
1133 Ok(RFIDEvent {
1134 uid: item.get_octet_string_owned(&[0]),
1135 })
1136 } else {
1137 Err(anyhow::anyhow!("Expected struct fields"))
1138 }
1139}
1140