1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct ChargingTargetSchedule {
15 pub day_of_week_for_sequence: Option<u8>,
16 pub charging_targets: Option<Vec<ChargingTarget>>,
17}
18
19#[derive(Debug, serde::Serialize)]
20pub struct ChargingTarget {
21 pub target_time_minutes_past_midnight: Option<u16>,
22 pub target_so_c: Option<u8>,
23 pub added_energy: Option<u8>,
24}
25
26pub fn encode_enable_charging(charging_enabled_until: Option<u64>, minimum_charge_current: u8, maximum_charge_current: u8) -> anyhow::Result<Vec<u8>> {
30 let tlv = tlv::TlvItemEnc {
31 tag: 0,
32 value: tlv::TlvItemValueEnc::StructInvisible(vec![
33 (0, tlv::TlvItemValueEnc::UInt64(charging_enabled_until.unwrap_or(0))).into(),
34 (1, tlv::TlvItemValueEnc::UInt8(minimum_charge_current)).into(),
35 (2, tlv::TlvItemValueEnc::UInt8(maximum_charge_current)).into(),
36 ]),
37 };
38 Ok(tlv.encode()?)
39}
40
41pub fn encode_enable_discharging(discharging_enabled_until: Option<u64>, maximum_discharge_current: u8) -> anyhow::Result<Vec<u8>> {
43 let tlv = tlv::TlvItemEnc {
44 tag: 0,
45 value: tlv::TlvItemValueEnc::StructInvisible(vec![
46 (0, tlv::TlvItemValueEnc::UInt64(discharging_enabled_until.unwrap_or(0))).into(),
47 (1, tlv::TlvItemValueEnc::UInt8(maximum_discharge_current)).into(),
48 ]),
49 };
50 Ok(tlv.encode()?)
51}
52
53pub fn encode_set_targets(charging_target_schedules: Vec<u8>) -> anyhow::Result<Vec<u8>> {
55 let tlv = tlv::TlvItemEnc {
56 tag: 0,
57 value: tlv::TlvItemValueEnc::StructInvisible(vec![
58 (0, tlv::TlvItemValueEnc::StructAnon(charging_target_schedules.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
59 ]),
60 };
61 Ok(tlv.encode()?)
62}
63
64pub fn decode_state(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
68 if let tlv::TlvItemValue::Int(v) = inp {
69 Ok(Some(*v as u8))
70 } else {
71 Ok(None)
72 }
73}
74
75pub fn decode_supply_state(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
77 if let tlv::TlvItemValue::Int(v) = inp {
78 Ok(*v as u8)
79 } else {
80 Err(anyhow::anyhow!("Expected Integer"))
81 }
82}
83
84pub fn decode_fault_state(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
86 if let tlv::TlvItemValue::Int(v) = inp {
87 Ok(*v as u8)
88 } else {
89 Err(anyhow::anyhow!("Expected Integer"))
90 }
91}
92
93pub fn decode_charging_enabled_until(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
95 if let tlv::TlvItemValue::Int(v) = inp {
96 Ok(Some(*v))
97 } else {
98 Ok(None)
99 }
100}
101
102pub fn decode_discharging_enabled_until(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
104 if let tlv::TlvItemValue::Int(v) = inp {
105 Ok(Some(*v))
106 } else {
107 Ok(None)
108 }
109}
110
111pub fn decode_circuit_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
113 if let tlv::TlvItemValue::Int(v) = inp {
114 Ok(*v as u8)
115 } else {
116 Err(anyhow::anyhow!("Expected Integer"))
117 }
118}
119
120pub fn decode_minimum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
122 if let tlv::TlvItemValue::Int(v) = inp {
123 Ok(*v as u8)
124 } else {
125 Err(anyhow::anyhow!("Expected Integer"))
126 }
127}
128
129pub fn decode_maximum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
131 if let tlv::TlvItemValue::Int(v) = inp {
132 Ok(*v as u8)
133 } else {
134 Err(anyhow::anyhow!("Expected Integer"))
135 }
136}
137
138pub fn decode_maximum_discharge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
140 if let tlv::TlvItemValue::Int(v) = inp {
141 Ok(*v as u8)
142 } else {
143 Err(anyhow::anyhow!("Expected Integer"))
144 }
145}
146
147pub fn decode_user_maximum_charge_current(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
149 if let tlv::TlvItemValue::Int(v) = inp {
150 Ok(*v as u8)
151 } else {
152 Err(anyhow::anyhow!("Expected Integer"))
153 }
154}
155
156pub fn decode_randomization_delay_window(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
158 if let tlv::TlvItemValue::Int(v) = inp {
159 Ok(*v as u8)
160 } else {
161 Err(anyhow::anyhow!("Expected Integer"))
162 }
163}
164
165pub fn decode_next_charge_start_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
167 if let tlv::TlvItemValue::Int(v) = inp {
168 Ok(Some(*v))
169 } else {
170 Ok(None)
171 }
172}
173
174pub fn decode_next_charge_target_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u64>> {
176 if let tlv::TlvItemValue::Int(v) = inp {
177 Ok(Some(*v))
178 } else {
179 Ok(None)
180 }
181}
182
183pub fn decode_next_charge_required_energy(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
185 if let tlv::TlvItemValue::Int(v) = inp {
186 Ok(Some(*v as u8))
187 } else {
188 Ok(None)
189 }
190}
191
192pub fn decode_next_charge_target_so_c(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
194 if let tlv::TlvItemValue::Int(v) = inp {
195 Ok(Some(*v as u8))
196 } else {
197 Ok(None)
198 }
199}
200
201pub fn decode_approximate_ev_efficiency(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
203 if let tlv::TlvItemValue::Int(v) = inp {
204 Ok(Some(*v as u16))
205 } else {
206 Ok(None)
207 }
208}
209
210pub fn decode_state_of_charge(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
212 if let tlv::TlvItemValue::Int(v) = inp {
213 Ok(Some(*v as u8))
214 } else {
215 Ok(None)
216 }
217}
218
219pub fn decode_battery_capacity(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
221 if let tlv::TlvItemValue::Int(v) = inp {
222 Ok(Some(*v as u8))
223 } else {
224 Ok(None)
225 }
226}
227
228pub fn decode_vehicle_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<String>> {
230 if let tlv::TlvItemValue::String(v) = inp {
231 Ok(Some(v.clone()))
232 } else {
233 Ok(None)
234 }
235}
236
237pub fn decode_session_id(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
239 if let tlv::TlvItemValue::Int(v) = inp {
240 Ok(Some(*v as u32))
241 } else {
242 Ok(None)
243 }
244}
245
246pub fn decode_session_duration(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
248 if let tlv::TlvItemValue::Int(v) = inp {
249 Ok(Some(*v as u8))
250 } else {
251 Ok(None)
252 }
253}
254
255pub fn decode_session_energy_charged(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
257 if let tlv::TlvItemValue::Int(v) = inp {
258 Ok(Some(*v as u8))
259 } else {
260 Ok(None)
261 }
262}
263
264pub fn decode_session_energy_discharged(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
266 if let tlv::TlvItemValue::Int(v) = inp {
267 Ok(Some(*v as u8))
268 } else {
269 Ok(None)
270 }
271}
272
273
274pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
286 if cluster_id != 0x0099 {
288 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0099, got {}\"}}", cluster_id);
289 }
290
291 match attribute_id {
292 0x0000 => {
293 match decode_state(tlv_value) {
294 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
295 Err(e) => format!("{{\"error\": \"{}\"}}", e),
296 }
297 }
298 0x0001 => {
299 match decode_supply_state(tlv_value) {
300 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
301 Err(e) => format!("{{\"error\": \"{}\"}}", e),
302 }
303 }
304 0x0002 => {
305 match decode_fault_state(tlv_value) {
306 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
307 Err(e) => format!("{{\"error\": \"{}\"}}", e),
308 }
309 }
310 0x0003 => {
311 match decode_charging_enabled_until(tlv_value) {
312 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
313 Err(e) => format!("{{\"error\": \"{}\"}}", e),
314 }
315 }
316 0x0004 => {
317 match decode_discharging_enabled_until(tlv_value) {
318 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
319 Err(e) => format!("{{\"error\": \"{}\"}}", e),
320 }
321 }
322 0x0005 => {
323 match decode_circuit_capacity(tlv_value) {
324 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
325 Err(e) => format!("{{\"error\": \"{}\"}}", e),
326 }
327 }
328 0x0006 => {
329 match decode_minimum_charge_current(tlv_value) {
330 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
331 Err(e) => format!("{{\"error\": \"{}\"}}", e),
332 }
333 }
334 0x0007 => {
335 match decode_maximum_charge_current(tlv_value) {
336 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
337 Err(e) => format!("{{\"error\": \"{}\"}}", e),
338 }
339 }
340 0x0008 => {
341 match decode_maximum_discharge_current(tlv_value) {
342 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
343 Err(e) => format!("{{\"error\": \"{}\"}}", e),
344 }
345 }
346 0x0009 => {
347 match decode_user_maximum_charge_current(tlv_value) {
348 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
349 Err(e) => format!("{{\"error\": \"{}\"}}", e),
350 }
351 }
352 0x000A => {
353 match decode_randomization_delay_window(tlv_value) {
354 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
355 Err(e) => format!("{{\"error\": \"{}\"}}", e),
356 }
357 }
358 0x0023 => {
359 match decode_next_charge_start_time(tlv_value) {
360 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
361 Err(e) => format!("{{\"error\": \"{}\"}}", e),
362 }
363 }
364 0x0024 => {
365 match decode_next_charge_target_time(tlv_value) {
366 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
367 Err(e) => format!("{{\"error\": \"{}\"}}", e),
368 }
369 }
370 0x0025 => {
371 match decode_next_charge_required_energy(tlv_value) {
372 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
373 Err(e) => format!("{{\"error\": \"{}\"}}", e),
374 }
375 }
376 0x0026 => {
377 match decode_next_charge_target_so_c(tlv_value) {
378 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
379 Err(e) => format!("{{\"error\": \"{}\"}}", e),
380 }
381 }
382 0x0027 => {
383 match decode_approximate_ev_efficiency(tlv_value) {
384 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
385 Err(e) => format!("{{\"error\": \"{}\"}}", e),
386 }
387 }
388 0x0030 => {
389 match decode_state_of_charge(tlv_value) {
390 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
391 Err(e) => format!("{{\"error\": \"{}\"}}", e),
392 }
393 }
394 0x0031 => {
395 match decode_battery_capacity(tlv_value) {
396 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
397 Err(e) => format!("{{\"error\": \"{}\"}}", e),
398 }
399 }
400 0x0032 => {
401 match decode_vehicle_id(tlv_value) {
402 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
403 Err(e) => format!("{{\"error\": \"{}\"}}", e),
404 }
405 }
406 0x0040 => {
407 match decode_session_id(tlv_value) {
408 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
409 Err(e) => format!("{{\"error\": \"{}\"}}", e),
410 }
411 }
412 0x0041 => {
413 match decode_session_duration(tlv_value) {
414 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
415 Err(e) => format!("{{\"error\": \"{}\"}}", e),
416 }
417 }
418 0x0042 => {
419 match decode_session_energy_charged(tlv_value) {
420 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
421 Err(e) => format!("{{\"error\": \"{}\"}}", e),
422 }
423 }
424 0x0043 => {
425 match decode_session_energy_discharged(tlv_value) {
426 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
427 Err(e) => format!("{{\"error\": \"{}\"}}", e),
428 }
429 }
430 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
431 }
432}
433
434pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
439 vec![
440 (0x0000, "State"),
441 (0x0001, "SupplyState"),
442 (0x0002, "FaultState"),
443 (0x0003, "ChargingEnabledUntil"),
444 (0x0004, "DischargingEnabledUntil"),
445 (0x0005, "CircuitCapacity"),
446 (0x0006, "MinimumChargeCurrent"),
447 (0x0007, "MaximumChargeCurrent"),
448 (0x0008, "MaximumDischargeCurrent"),
449 (0x0009, "UserMaximumChargeCurrent"),
450 (0x000A, "RandomizationDelayWindow"),
451 (0x0023, "NextChargeStartTime"),
452 (0x0024, "NextChargeTargetTime"),
453 (0x0025, "NextChargeRequiredEnergy"),
454 (0x0026, "NextChargeTargetSoC"),
455 (0x0027, "ApproximateEVEfficiency"),
456 (0x0030, "StateOfCharge"),
457 (0x0031, "BatteryCapacity"),
458 (0x0032, "VehicleID"),
459 (0x0040, "SessionID"),
460 (0x0041, "SessionDuration"),
461 (0x0042, "SessionEnergyCharged"),
462 (0x0043, "SessionEnergyDischarged"),
463 ]
464}
465