matc/clusters/codec/
device_energy_management.rs

1//! Generated Matter TLV encoders and decoders for Device Energy Management Cluster
2//! Cluster ID: 0x0098
3//! 
4//! This file is automatically generated from DeviceEnergyManagement.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Struct definitions
12
13#[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
86// Command encoders
87
88/// Encode PowerAdjustRequest command (0x00)
89pub 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
101/// Encode StartTimeAdjustRequest command (0x02)
102pub 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
113/// Encode PauseRequest command (0x03)
114pub 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
125/// Encode ModifyForecastRequest command (0x05)
126pub 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
138/// Encode RequestConstraintBasedForecast command (0x06)
139pub 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
150// Attribute decoders
151
152/// Decode ESAType attribute (0x0000)
153pub 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
161/// Decode ESACanGenerate attribute (0x0001)
162pub 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
170/// Decode ESAState attribute (0x0002)
171pub 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
179/// Decode AbsMinPower attribute (0x0003)
180pub 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
188/// Decode AbsMaxPower attribute (0x0004)
189pub 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
197/// Decode PowerAdjustmentCapability attribute (0x0005)
198pub fn decode_power_adjustment_capability(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<PowerAdjustCapability>> {
199    if let tlv::TlvItemValue::List(_fields) = inp {
200        // Struct with fields
201        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 if let tlv::TlvItemValue::Null = inp {
222    //    // Null value for nullable struct
223    //    Ok(None)
224    } else {
225    Ok(None)
226    //    Err(anyhow::anyhow!("Expected struct fields or null"))
227    }
228}
229
230/// Decode Forecast attribute (0x0006)
231pub fn decode_forecast(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<Forecast>> {
232    if let tlv::TlvItemValue::List(_fields) = inp {
233        // Struct with fields
234        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 if let tlv::TlvItemValue::Null = inp {
291    //    // Null value for nullable struct
292    //    Ok(None)
293    } else {
294    Ok(None)
295    //    Err(anyhow::anyhow!("Expected struct fields or null"))
296    }
297}
298
299/// Decode OptOutState attribute (0x0007)
300pub 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
309// JSON dispatcher function
310
311/// Decode attribute value and return as JSON string
312/// 
313/// # Parameters
314/// * `cluster_id` - The cluster identifier
315/// * `attribute_id` - The attribute identifier
316/// * `tlv_value` - The TLV value to decode
317/// 
318/// # Returns
319/// JSON string representation of the decoded value or error
320pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
321    // Verify this is the correct cluster
322    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
379/// Get list of all attributes supported by this cluster
380/// 
381/// # Returns
382/// Vector of tuples containing (attribute_id, attribute_name)
383pub 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