matc/clusters/codec/
electrical_power_measurement.rs

1//! Matter TLV encoders and decoders for Electrical Power Measurement Cluster
2//! Cluster ID: 0x0090
3//!
4//! This file is automatically generated from ElectricalPowerMeasurement.xml
5
6use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11// Enum definitions
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum MeasurementType {
16    Unspecified = 0,
17    /// Voltage in millivolts (mV)
18    Voltage = 1,
19    /// Active current in milliamps (mA)
20    Activecurrent = 2,
21    /// Reactive current in milliamps (mA)
22    Reactivecurrent = 3,
23    /// Apparent current in milliamps (mA)
24    Apparentcurrent = 4,
25    /// Active power in milliwatts (mW)
26    Activepower = 5,
27    /// Reactive power in millivolt-amps reactive (mVAR)
28    Reactivepower = 6,
29    /// Apparent power in millivolt-amps (mVA)
30    Apparentpower = 7,
31    /// Root mean squared voltage in millivolts (mV)
32    Rmsvoltage = 8,
33    /// Root mean squared current in milliamps (mA)
34    Rmscurrent = 9,
35    /// Root mean squared power in milliwatts (mW)
36    Rmspower = 10,
37    /// AC frequency in millihertz (mHz)
38    Frequency = 11,
39    /// Power Factor ratio in +/- 1/100ths of a percent.
40    Powerfactor = 12,
41    /// AC neutral current in milliamps (mA)
42    Neutralcurrent = 13,
43    /// Electrical energy in milliwatt-hours (mWh)
44    Electricalenergy = 14,
45    /// Reactive power in millivolt-amp-hours reactive (mVARh)
46    Reactiveenergy = 15,
47    /// Apparent power in millivolt-amp-hours (mVAh)
48    Apparentenergy = 16,
49}
50
51impl MeasurementType {
52    /// Convert from u8 value
53    pub fn from_u8(value: u8) -> Option<Self> {
54        match value {
55            0 => Some(MeasurementType::Unspecified),
56            1 => Some(MeasurementType::Voltage),
57            2 => Some(MeasurementType::Activecurrent),
58            3 => Some(MeasurementType::Reactivecurrent),
59            4 => Some(MeasurementType::Apparentcurrent),
60            5 => Some(MeasurementType::Activepower),
61            6 => Some(MeasurementType::Reactivepower),
62            7 => Some(MeasurementType::Apparentpower),
63            8 => Some(MeasurementType::Rmsvoltage),
64            9 => Some(MeasurementType::Rmscurrent),
65            10 => Some(MeasurementType::Rmspower),
66            11 => Some(MeasurementType::Frequency),
67            12 => Some(MeasurementType::Powerfactor),
68            13 => Some(MeasurementType::Neutralcurrent),
69            14 => Some(MeasurementType::Electricalenergy),
70            15 => Some(MeasurementType::Reactiveenergy),
71            16 => Some(MeasurementType::Apparentenergy),
72            _ => None,
73        }
74    }
75
76    /// Convert to u8 value
77    pub fn to_u8(self) -> u8 {
78        self as u8
79    }
80}
81
82impl From<MeasurementType> for u8 {
83    fn from(val: MeasurementType) -> Self {
84        val as u8
85    }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
89#[repr(u8)]
90pub enum PowerMode {
91    Unknown = 0,
92    /// Direct current
93    Dc = 1,
94    /// Alternating current, either single-phase or polyphase
95    Ac = 2,
96}
97
98impl PowerMode {
99    /// Convert from u8 value
100    pub fn from_u8(value: u8) -> Option<Self> {
101        match value {
102            0 => Some(PowerMode::Unknown),
103            1 => Some(PowerMode::Dc),
104            2 => Some(PowerMode::Ac),
105            _ => None,
106        }
107    }
108
109    /// Convert to u8 value
110    pub fn to_u8(self) -> u8 {
111        self as u8
112    }
113}
114
115impl From<PowerMode> for u8 {
116    fn from(val: PowerMode) -> Self {
117        val as u8
118    }
119}
120
121// Struct definitions
122
123#[derive(Debug, serde::Serialize)]
124pub struct HarmonicMeasurement {
125    pub order: Option<u8>,
126    pub measurement: Option<i64>,
127}
128
129#[derive(Debug, serde::Serialize)]
130pub struct MeasurementAccuracyRange {
131    pub range_min: Option<i64>,
132    pub range_max: Option<i64>,
133    pub percent_max: Option<u8>,
134    pub percent_min: Option<u8>,
135    pub percent_typical: Option<u8>,
136    pub fixed_max: Option<u64>,
137    pub fixed_min: Option<u64>,
138    pub fixed_typical: Option<u64>,
139}
140
141#[derive(Debug, serde::Serialize)]
142pub struct MeasurementAccuracy {
143    pub measurement_type: Option<MeasurementType>,
144    pub measured: Option<bool>,
145    pub min_measured_value: Option<i64>,
146    pub max_measured_value: Option<i64>,
147    pub accuracy_ranges: Option<Vec<MeasurementAccuracyRange>>,
148}
149
150#[derive(Debug, serde::Serialize)]
151pub struct MeasurementRange {
152    pub measurement_type: Option<MeasurementType>,
153    pub min: Option<i64>,
154    pub max: Option<i64>,
155    pub start_timestamp: Option<u64>,
156    pub end_timestamp: Option<u64>,
157    pub min_timestamp: Option<u64>,
158    pub max_timestamp: Option<u64>,
159    pub start_systime: Option<u8>,
160    pub end_systime: Option<u8>,
161    pub min_systime: Option<u8>,
162    pub max_systime: Option<u8>,
163}
164
165// Attribute decoders
166
167/// Decode PowerMode attribute (0x0000)
168pub fn decode_power_mode(inp: &tlv::TlvItemValue) -> anyhow::Result<PowerMode> {
169    if let tlv::TlvItemValue::Int(v) = inp {
170        PowerMode::from_u8(*v as u8).ok_or_else(|| anyhow::anyhow!("Invalid enum value"))
171    } else {
172        Err(anyhow::anyhow!("Expected Integer"))
173    }
174}
175
176/// Decode NumberOfMeasurementTypes attribute (0x0001)
177pub fn decode_number_of_measurement_types(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
178    if let tlv::TlvItemValue::Int(v) = inp {
179        Ok(*v as u8)
180    } else {
181        Err(anyhow::anyhow!("Expected UInt8"))
182    }
183}
184
185/// Decode Accuracy attribute (0x0002)
186pub fn decode_accuracy(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<MeasurementAccuracy>> {
187    let mut res = Vec::new();
188    if let tlv::TlvItemValue::List(v) = inp {
189        for item in v {
190            res.push(MeasurementAccuracy {
191                measurement_type: item.get_int(&[0]).and_then(|v| MeasurementType::from_u8(v as u8)),
192                measured: item.get_bool(&[1]),
193                min_measured_value: item.get_int(&[2]).map(|v| v as i64),
194                max_measured_value: item.get_int(&[3]).map(|v| v as i64),
195                accuracy_ranges: {
196                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[4]) {
197                        let mut items = Vec::new();
198                        for list_item in l {
199                            items.push(MeasurementAccuracyRange {
200                range_min: list_item.get_int(&[0]).map(|v| v as i64),
201                range_max: list_item.get_int(&[1]).map(|v| v as i64),
202                percent_max: list_item.get_int(&[2]).map(|v| v as u8),
203                percent_min: list_item.get_int(&[3]).map(|v| v as u8),
204                percent_typical: list_item.get_int(&[4]).map(|v| v as u8),
205                fixed_max: list_item.get_int(&[5]),
206                fixed_min: list_item.get_int(&[6]),
207                fixed_typical: list_item.get_int(&[7]),
208                            });
209                        }
210                        Some(items)
211                    } else {
212                        None
213                    }
214                },
215            });
216        }
217    }
218    Ok(res)
219}
220
221/// Decode Ranges attribute (0x0003)
222pub fn decode_ranges(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<MeasurementRange>> {
223    let mut res = Vec::new();
224    if let tlv::TlvItemValue::List(v) = inp {
225        for item in v {
226            res.push(MeasurementRange {
227                measurement_type: item.get_int(&[0]).and_then(|v| MeasurementType::from_u8(v as u8)),
228                min: item.get_int(&[1]).map(|v| v as i64),
229                max: item.get_int(&[2]).map(|v| v as i64),
230                start_timestamp: item.get_int(&[3]),
231                end_timestamp: item.get_int(&[4]),
232                min_timestamp: item.get_int(&[5]),
233                max_timestamp: item.get_int(&[6]),
234                start_systime: item.get_int(&[7]).map(|v| v as u8),
235                end_systime: item.get_int(&[8]).map(|v| v as u8),
236                min_systime: item.get_int(&[9]).map(|v| v as u8),
237                max_systime: item.get_int(&[10]).map(|v| v as u8),
238            });
239        }
240    }
241    Ok(res)
242}
243
244/// Decode Voltage attribute (0x0004)
245pub fn decode_voltage(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
246    if let tlv::TlvItemValue::Int(v) = inp {
247        Ok(Some(*v as u8))
248    } else {
249        Ok(None)
250    }
251}
252
253/// Decode ActiveCurrent attribute (0x0005)
254pub fn decode_active_current(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
255    if let tlv::TlvItemValue::Int(v) = inp {
256        Ok(Some(*v as u8))
257    } else {
258        Ok(None)
259    }
260}
261
262/// Decode ReactiveCurrent attribute (0x0006)
263pub fn decode_reactive_current(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
264    if let tlv::TlvItemValue::Int(v) = inp {
265        Ok(Some(*v as u8))
266    } else {
267        Ok(None)
268    }
269}
270
271/// Decode ApparentCurrent attribute (0x0007)
272pub fn decode_apparent_current(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
273    if let tlv::TlvItemValue::Int(v) = inp {
274        Ok(Some(*v as u8))
275    } else {
276        Ok(None)
277    }
278}
279
280/// Decode ActivePower attribute (0x0008)
281pub fn decode_active_power(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
282    if let tlv::TlvItemValue::Int(v) = inp {
283        Ok(Some(*v as u32))
284    } else {
285        Ok(None)
286    }
287}
288
289/// Decode ReactivePower attribute (0x0009)
290pub fn decode_reactive_power(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
291    if let tlv::TlvItemValue::Int(v) = inp {
292        Ok(Some(*v as u8))
293    } else {
294        Ok(None)
295    }
296}
297
298/// Decode ApparentPower attribute (0x000A)
299pub fn decode_apparent_power(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
300    if let tlv::TlvItemValue::Int(v) = inp {
301        Ok(Some(*v as u8))
302    } else {
303        Ok(None)
304    }
305}
306
307/// Decode RMSVoltage attribute (0x000B)
308pub fn decode_rms_voltage(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
309    if let tlv::TlvItemValue::Int(v) = inp {
310        Ok(Some(*v as u8))
311    } else {
312        Ok(None)
313    }
314}
315
316/// Decode RMSCurrent attribute (0x000C)
317pub fn decode_rms_current(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
318    if let tlv::TlvItemValue::Int(v) = inp {
319        Ok(Some(*v as u8))
320    } else {
321        Ok(None)
322    }
323}
324
325/// Decode RMSPower attribute (0x000D)
326pub fn decode_rms_power(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u32>> {
327    if let tlv::TlvItemValue::Int(v) = inp {
328        Ok(Some(*v as u32))
329    } else {
330        Ok(None)
331    }
332}
333
334/// Decode Frequency attribute (0x000E)
335pub fn decode_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i64>> {
336    if let tlv::TlvItemValue::Int(v) = inp {
337        Ok(Some(*v as i64))
338    } else {
339        Ok(None)
340    }
341}
342
343/// Decode HarmonicCurrents attribute (0x000F)
344pub fn decode_harmonic_currents(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<HarmonicMeasurement>> {
345    let mut res = Vec::new();
346    if let tlv::TlvItemValue::List(v) = inp {
347        for item in v {
348            res.push(HarmonicMeasurement {
349                order: item.get_int(&[0]).map(|v| v as u8),
350                measurement: item.get_int(&[1]).map(|v| v as i64),
351            });
352        }
353    }
354    Ok(res)
355}
356
357/// Decode HarmonicPhases attribute (0x0010)
358pub fn decode_harmonic_phases(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<HarmonicMeasurement>> {
359    let mut res = Vec::new();
360    if let tlv::TlvItemValue::List(v) = inp {
361        for item in v {
362            res.push(HarmonicMeasurement {
363                order: item.get_int(&[0]).map(|v| v as u8),
364                measurement: item.get_int(&[1]).map(|v| v as i64),
365            });
366        }
367    }
368    Ok(res)
369}
370
371/// Decode PowerFactor attribute (0x0011)
372pub fn decode_power_factor(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<i64>> {
373    if let tlv::TlvItemValue::Int(v) = inp {
374        Ok(Some(*v as i64))
375    } else {
376        Ok(None)
377    }
378}
379
380/// Decode NeutralCurrent attribute (0x0012)
381pub fn decode_neutral_current(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
382    if let tlv::TlvItemValue::Int(v) = inp {
383        Ok(Some(*v as u8))
384    } else {
385        Ok(None)
386    }
387}
388
389
390// JSON dispatcher function
391
392/// Decode attribute value and return as JSON string
393///
394/// # Parameters
395/// * `cluster_id` - The cluster identifier
396/// * `attribute_id` - The attribute identifier
397/// * `tlv_value` - The TLV value to decode
398///
399/// # Returns
400/// JSON string representation of the decoded value or error
401pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
402    // Verify this is the correct cluster
403    if cluster_id != 0x0090 {
404        return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0090, got {}\"}}", cluster_id);
405    }
406
407    match attribute_id {
408        0x0000 => {
409            match decode_power_mode(tlv_value) {
410                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
411                Err(e) => format!("{{\"error\": \"{}\"}}", e),
412            }
413        }
414        0x0001 => {
415            match decode_number_of_measurement_types(tlv_value) {
416                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
417                Err(e) => format!("{{\"error\": \"{}\"}}", e),
418            }
419        }
420        0x0002 => {
421            match decode_accuracy(tlv_value) {
422                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
423                Err(e) => format!("{{\"error\": \"{}\"}}", e),
424            }
425        }
426        0x0003 => {
427            match decode_ranges(tlv_value) {
428                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
429                Err(e) => format!("{{\"error\": \"{}\"}}", e),
430            }
431        }
432        0x0004 => {
433            match decode_voltage(tlv_value) {
434                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
435                Err(e) => format!("{{\"error\": \"{}\"}}", e),
436            }
437        }
438        0x0005 => {
439            match decode_active_current(tlv_value) {
440                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
441                Err(e) => format!("{{\"error\": \"{}\"}}", e),
442            }
443        }
444        0x0006 => {
445            match decode_reactive_current(tlv_value) {
446                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
447                Err(e) => format!("{{\"error\": \"{}\"}}", e),
448            }
449        }
450        0x0007 => {
451            match decode_apparent_current(tlv_value) {
452                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
453                Err(e) => format!("{{\"error\": \"{}\"}}", e),
454            }
455        }
456        0x0008 => {
457            match decode_active_power(tlv_value) {
458                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
459                Err(e) => format!("{{\"error\": \"{}\"}}", e),
460            }
461        }
462        0x0009 => {
463            match decode_reactive_power(tlv_value) {
464                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
465                Err(e) => format!("{{\"error\": \"{}\"}}", e),
466            }
467        }
468        0x000A => {
469            match decode_apparent_power(tlv_value) {
470                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
471                Err(e) => format!("{{\"error\": \"{}\"}}", e),
472            }
473        }
474        0x000B => {
475            match decode_rms_voltage(tlv_value) {
476                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
477                Err(e) => format!("{{\"error\": \"{}\"}}", e),
478            }
479        }
480        0x000C => {
481            match decode_rms_current(tlv_value) {
482                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
483                Err(e) => format!("{{\"error\": \"{}\"}}", e),
484            }
485        }
486        0x000D => {
487            match decode_rms_power(tlv_value) {
488                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
489                Err(e) => format!("{{\"error\": \"{}\"}}", e),
490            }
491        }
492        0x000E => {
493            match decode_frequency(tlv_value) {
494                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
495                Err(e) => format!("{{\"error\": \"{}\"}}", e),
496            }
497        }
498        0x000F => {
499            match decode_harmonic_currents(tlv_value) {
500                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
501                Err(e) => format!("{{\"error\": \"{}\"}}", e),
502            }
503        }
504        0x0010 => {
505            match decode_harmonic_phases(tlv_value) {
506                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
507                Err(e) => format!("{{\"error\": \"{}\"}}", e),
508            }
509        }
510        0x0011 => {
511            match decode_power_factor(tlv_value) {
512                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
513                Err(e) => format!("{{\"error\": \"{}\"}}", e),
514            }
515        }
516        0x0012 => {
517            match decode_neutral_current(tlv_value) {
518                Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
519                Err(e) => format!("{{\"error\": \"{}\"}}", e),
520            }
521        }
522        _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
523    }
524}
525
526/// Get list of all attributes supported by this cluster
527///
528/// # Returns
529/// Vector of tuples containing (attribute_id, attribute_name)
530pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
531    vec![
532        (0x0000, "PowerMode"),
533        (0x0001, "NumberOfMeasurementTypes"),
534        (0x0002, "Accuracy"),
535        (0x0003, "Ranges"),
536        (0x0004, "Voltage"),
537        (0x0005, "ActiveCurrent"),
538        (0x0006, "ReactiveCurrent"),
539        (0x0007, "ApparentCurrent"),
540        (0x0008, "ActivePower"),
541        (0x0009, "ReactivePower"),
542        (0x000A, "ApparentPower"),
543        (0x000B, "RMSVoltage"),
544        (0x000C, "RMSCurrent"),
545        (0x000D, "RMSPower"),
546        (0x000E, "Frequency"),
547        (0x000F, "HarmonicCurrents"),
548        (0x0010, "HarmonicPhases"),
549        (0x0011, "PowerFactor"),
550        (0x0012, "NeutralCurrent"),
551    ]
552}
553
554#[derive(Debug, serde::Serialize)]
555pub struct MeasurementPeriodRangesEvent {
556    pub ranges: Option<Vec<MeasurementRange>>,
557}
558
559// Event decoders
560
561/// Decode MeasurementPeriodRanges event (0x00, priority: info)
562pub fn decode_measurement_period_ranges_event(inp: &tlv::TlvItemValue) -> anyhow::Result<MeasurementPeriodRangesEvent> {
563    if let tlv::TlvItemValue::List(_fields) = inp {
564        let item = tlv::TlvItem { tag: 0, value: inp.clone() };
565        Ok(MeasurementPeriodRangesEvent {
566                                ranges: {
567                    if let Some(tlv::TlvItemValue::List(l)) = item.get(&[0]) {
568                        let mut items = Vec::new();
569                        for list_item in l {
570                            items.push(MeasurementRange {
571                measurement_type: list_item.get_int(&[0]).and_then(|v| MeasurementType::from_u8(v as u8)),
572                min: list_item.get_int(&[1]).map(|v| v as i64),
573                max: list_item.get_int(&[2]).map(|v| v as i64),
574                start_timestamp: list_item.get_int(&[3]),
575                end_timestamp: list_item.get_int(&[4]),
576                min_timestamp: list_item.get_int(&[5]),
577                max_timestamp: list_item.get_int(&[6]),
578                start_systime: list_item.get_int(&[7]).map(|v| v as u8),
579                end_systime: list_item.get_int(&[8]).map(|v| v as u8),
580                min_systime: list_item.get_int(&[9]).map(|v| v as u8),
581                max_systime: list_item.get_int(&[10]).map(|v| v as u8),
582                            });
583                        }
584                        Some(items)
585                    } else {
586                        None
587                    }
588                },
589        })
590    } else {
591        Err(anyhow::anyhow!("Expected struct fields"))
592    }
593}
594