1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct Constraints {
15 pub start_time: Option<u64>,
16 pub duration: Option<u8>,
17 pub nominal_power: Option<u8>,
18 pub maximum_energy: Option<u8>,
19 pub load_control: Option<i8>,
20}
21
22#[derive(Debug, serde::Serialize)]
23pub struct Cost {
24 pub cost_type: Option<u8>,
25 pub value: Option<i32>,
26 pub decimal_points: Option<u8>,
27 pub currency: Option<u16>,
28}
29
30#[derive(Debug, serde::Serialize)]
31pub struct Forecast {
32 pub forecast_id: Option<u32>,
33 pub active_slot_number: Option<u16>,
34 pub start_time: Option<u64>,
35 pub end_time: Option<u64>,
36 pub earliest_start_time: Option<u64>,
37 pub latest_end_time: Option<u64>,
38 pub is_pausable: Option<bool>,
39 pub slots: Option<Vec<Slot>>,
40 pub forecast_update_reason: Option<u8>,
41}
42
43#[derive(Debug, serde::Serialize)]
44pub struct PowerAdjustCapability {
45 pub power_adjust_capability: Option<Vec<PowerAdjust>>,
46 pub cause: Option<u8>,
47}
48
49#[derive(Debug, serde::Serialize)]
50pub struct PowerAdjust {
51 pub min_power: Option<u8>,
52 pub max_power: Option<u8>,
53 pub min_duration: Option<u8>,
54 pub max_duration: Option<u8>,
55}
56
57#[derive(Debug, serde::Serialize)]
58pub struct SlotAdjustment {
59 pub slot_index: Option<u8>,
60 pub nominal_power: Option<u8>,
61 pub duration: Option<u8>,
62}
63
64#[derive(Debug, serde::Serialize)]
65pub struct Slot {
66 pub min_duration: Option<u8>,
67 pub max_duration: Option<u8>,
68 pub default_duration: Option<u8>,
69 pub elapsed_slot_time: Option<u8>,
70 pub remaining_slot_time: Option<u8>,
71 pub slot_is_pausable: Option<bool>,
72 pub min_pause_duration: Option<u8>,
73 pub max_pause_duration: Option<u8>,
74 pub manufacturer_esa_state: Option<u16>,
75 pub nominal_power: Option<u8>,
76 pub min_power: Option<u8>,
77 pub max_power: Option<u8>,
78 pub nominal_energy: Option<u8>,
79 pub costs: Option<Vec<Cost>>,
80 pub min_power_adjustment: Option<u8>,
81 pub max_power_adjustment: Option<u8>,
82 pub min_duration_adjustment: Option<u8>,
83 pub max_duration_adjustment: Option<u8>,
84}
85
86pub fn encode_power_adjust_request(power: u8, duration: u8, cause: u8) -> anyhow::Result<Vec<u8>> {
90 let tlv = tlv::TlvItemEnc {
91 tag: 0,
92 value: tlv::TlvItemValueEnc::StructInvisible(vec![
93 (0, tlv::TlvItemValueEnc::UInt8(power)).into(),
94 (1, tlv::TlvItemValueEnc::UInt8(duration)).into(),
95 (2, tlv::TlvItemValueEnc::UInt8(cause)).into(),
96 ]),
97 };
98 Ok(tlv.encode()?)
99}
100
101pub fn encode_start_time_adjust_request(requested_start_time: u64, cause: u8) -> anyhow::Result<Vec<u8>> {
103 let tlv = tlv::TlvItemEnc {
104 tag: 0,
105 value: tlv::TlvItemValueEnc::StructInvisible(vec![
106 (0, tlv::TlvItemValueEnc::UInt64(requested_start_time)).into(),
107 (1, tlv::TlvItemValueEnc::UInt8(cause)).into(),
108 ]),
109 };
110 Ok(tlv.encode()?)
111}
112
113pub fn encode_pause_request(duration: u8, cause: u8) -> anyhow::Result<Vec<u8>> {
115 let tlv = tlv::TlvItemEnc {
116 tag: 0,
117 value: tlv::TlvItemValueEnc::StructInvisible(vec![
118 (0, tlv::TlvItemValueEnc::UInt8(duration)).into(),
119 (1, tlv::TlvItemValueEnc::UInt8(cause)).into(),
120 ]),
121 };
122 Ok(tlv.encode()?)
123}
124
125pub fn encode_modify_forecast_request(forecast_id: u32, slot_adjustments: Vec<u8>, cause: u8) -> anyhow::Result<Vec<u8>> {
127 let tlv = tlv::TlvItemEnc {
128 tag: 0,
129 value: tlv::TlvItemValueEnc::StructInvisible(vec![
130 (0, tlv::TlvItemValueEnc::UInt32(forecast_id)).into(),
131 (1, tlv::TlvItemValueEnc::StructAnon(slot_adjustments.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
132 (2, tlv::TlvItemValueEnc::UInt8(cause)).into(),
133 ]),
134 };
135 Ok(tlv.encode()?)
136}
137
138pub fn encode_request_constraint_based_forecast(constraints: Vec<u8>, cause: u8) -> anyhow::Result<Vec<u8>> {
140 let tlv = tlv::TlvItemEnc {
141 tag: 0,
142 value: tlv::TlvItemValueEnc::StructInvisible(vec![
143 (0, tlv::TlvItemValueEnc::StructAnon(constraints.into_iter().map(|v| (0, tlv::TlvItemValueEnc::UInt8(v)).into()).collect())).into(),
144 (1, tlv::TlvItemValueEnc::UInt8(cause)).into(),
145 ]),
146 };
147 Ok(tlv.encode()?)
148}
149
150pub fn decode_esa_type(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
154 if let tlv::TlvItemValue::Int(v) = inp {
155 Ok(*v as u8)
156 } else {
157 Err(anyhow::anyhow!("Expected Integer"))
158 }
159}
160
161pub fn decode_esa_can_generate(inp: &tlv::TlvItemValue) -> anyhow::Result<bool> {
163 if let tlv::TlvItemValue::Bool(v) = inp {
164 Ok(*v)
165 } else {
166 Err(anyhow::anyhow!("Expected Bool"))
167 }
168}
169
170pub fn decode_esa_state(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
172 if let tlv::TlvItemValue::Int(v) = inp {
173 Ok(*v as u8)
174 } else {
175 Err(anyhow::anyhow!("Expected Integer"))
176 }
177}
178
179pub fn decode_abs_min_power(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
181 if let tlv::TlvItemValue::Int(v) = inp {
182 Ok(*v as u8)
183 } else {
184 Err(anyhow::anyhow!("Expected Integer"))
185 }
186}
187
188pub fn decode_abs_max_power(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
190 if let tlv::TlvItemValue::Int(v) = inp {
191 Ok(*v as u8)
192 } else {
193 Err(anyhow::anyhow!("Expected Integer"))
194 }
195}
196
197pub fn decode_power_adjustment_capability(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<PowerAdjustCapability>> {
199 if let tlv::TlvItemValue::List(_fields) = inp {
200 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
202 Ok(Some(PowerAdjustCapability {
203 power_adjust_capability: {
204 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
205 let mut items = Vec::new();
206 for list_item in l {
207 items.push(PowerAdjust {
208 min_power: list_item.get_int(&[0]).map(|v| v as u8),
209 max_power: list_item.get_int(&[1]).map(|v| v as u8),
210 min_duration: list_item.get_int(&[2]).map(|v| v as u8),
211 max_duration: list_item.get_int(&[3]).map(|v| v as u8),
212 });
213 }
214 Some(items)
215 } else {
216 None
217 }
218 },
219 cause: item.get_int(&[1]).map(|v| v as u8),
220 }))
221 } else {
225 Ok(None)
226 }
228}
229
230pub fn decode_forecast(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Forecast>> {
232 if let tlv::TlvItemValue::List(_fields) = inp {
233 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
235 Ok(Some(Forecast {
236 forecast_id: item.get_int(&[0]).map(|v| v as u32),
237 active_slot_number: item.get_int(&[1]).map(|v| v as u16),
238 start_time: item.get_int(&[2]),
239 end_time: item.get_int(&[3]),
240 earliest_start_time: item.get_int(&[4]),
241 latest_end_time: item.get_int(&[5]),
242 is_pausable: item.get_bool(&[6]),
243 slots: {
244 if let Some(tlv::TlvItemValue::List(l)) = item.get(&[7]) {
245 let mut items = Vec::new();
246 for list_item in l {
247 items.push(Slot {
248 min_duration: list_item.get_int(&[0]).map(|v| v as u8),
249 max_duration: list_item.get_int(&[1]).map(|v| v as u8),
250 default_duration: list_item.get_int(&[2]).map(|v| v as u8),
251 elapsed_slot_time: list_item.get_int(&[3]).map(|v| v as u8),
252 remaining_slot_time: list_item.get_int(&[4]).map(|v| v as u8),
253 slot_is_pausable: list_item.get_bool(&[5]),
254 min_pause_duration: list_item.get_int(&[6]).map(|v| v as u8),
255 max_pause_duration: list_item.get_int(&[7]).map(|v| v as u8),
256 manufacturer_esa_state: list_item.get_int(&[8]).map(|v| v as u16),
257 nominal_power: list_item.get_int(&[9]).map(|v| v as u8),
258 min_power: list_item.get_int(&[10]).map(|v| v as u8),
259 max_power: list_item.get_int(&[11]).map(|v| v as u8),
260 nominal_energy: list_item.get_int(&[12]).map(|v| v as u8),
261 costs: {
262 if let Some(tlv::TlvItemValue::List(l)) = list_item.get(&[13]) {
263 let mut items = Vec::new();
264 for list_item in l {
265 items.push(Cost {
266 cost_type: list_item.get_int(&[0]).map(|v| v as u8),
267 value: list_item.get_int(&[1]).map(|v| v as i32),
268 decimal_points: list_item.get_int(&[2]).map(|v| v as u8),
269 currency: list_item.get_int(&[3]).map(|v| v as u16),
270 });
271 }
272 Some(items)
273 } else {
274 None
275 }
276 },
277 min_power_adjustment: list_item.get_int(&[14]).map(|v| v as u8),
278 max_power_adjustment: list_item.get_int(&[15]).map(|v| v as u8),
279 min_duration_adjustment: list_item.get_int(&[16]).map(|v| v as u8),
280 max_duration_adjustment: list_item.get_int(&[17]).map(|v| v as u8),
281 });
282 }
283 Some(items)
284 } else {
285 None
286 }
287 },
288 forecast_update_reason: item.get_int(&[8]).map(|v| v as u8),
289 }))
290 } else {
294 Ok(None)
295 }
297}
298
299pub fn decode_opt_out_state(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
301 if let tlv::TlvItemValue::Int(v) = inp {
302 Ok(*v as u8)
303 } else {
304 Err(anyhow::anyhow!("Expected Integer"))
305 }
306}
307
308
309pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
321 if cluster_id != 0x0098 {
323 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0098, got {}\"}}", cluster_id);
324 }
325
326 match attribute_id {
327 0x0000 => {
328 match decode_esa_type(tlv_value) {
329 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
330 Err(e) => format!("{{\"error\": \"{}\"}}", e),
331 }
332 }
333 0x0001 => {
334 match decode_esa_can_generate(tlv_value) {
335 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
336 Err(e) => format!("{{\"error\": \"{}\"}}", e),
337 }
338 }
339 0x0002 => {
340 match decode_esa_state(tlv_value) {
341 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
342 Err(e) => format!("{{\"error\": \"{}\"}}", e),
343 }
344 }
345 0x0003 => {
346 match decode_abs_min_power(tlv_value) {
347 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
348 Err(e) => format!("{{\"error\": \"{}\"}}", e),
349 }
350 }
351 0x0004 => {
352 match decode_abs_max_power(tlv_value) {
353 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
354 Err(e) => format!("{{\"error\": \"{}\"}}", e),
355 }
356 }
357 0x0005 => {
358 match decode_power_adjustment_capability(tlv_value) {
359 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
360 Err(e) => format!("{{\"error\": \"{}\"}}", e),
361 }
362 }
363 0x0006 => {
364 match decode_forecast(tlv_value) {
365 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
366 Err(e) => format!("{{\"error\": \"{}\"}}", e),
367 }
368 }
369 0x0007 => {
370 match decode_opt_out_state(tlv_value) {
371 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
372 Err(e) => format!("{{\"error\": \"{}\"}}", e),
373 }
374 }
375 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
376 }
377}
378
379pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
384 vec![
385 (0x0000, "ESAType"),
386 (0x0001, "ESACanGenerate"),
387 (0x0002, "ESAState"),
388 (0x0003, "AbsMinPower"),
389 (0x0004, "AbsMaxPower"),
390 (0x0005, "PowerAdjustmentCapability"),
391 (0x0006, "Forecast"),
392 (0x0007, "OptOutState"),
393 ]
394}
395