matc/clusters/codec/
electrical_grid_conditions.rs1#![allow(clippy::too_many_arguments)]
7
8use crate::tlv;
9use anyhow;
10use serde_json;
11
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
16#[repr(u8)]
17pub enum ThreeLevel {
18 Low = 0,
20 Medium = 1,
22 High = 2,
24}
25
26impl ThreeLevel {
27 pub fn from_u8(value: u8) -> Option<Self> {
29 match value {
30 0 => Some(ThreeLevel::Low),
31 1 => Some(ThreeLevel::Medium),
32 2 => Some(ThreeLevel::High),
33 _ => None,
34 }
35 }
36
37 pub fn to_u8(self) -> u8 {
39 self as u8
40 }
41}
42
43impl From<ThreeLevel> for u8 {
44 fn from(val: ThreeLevel) -> Self {
45 val as u8
46 }
47}
48
49#[derive(Debug, serde::Serialize)]
52pub struct ElectricalGridConditions {
53 pub period_start: Option<u64>,
54 pub period_end: Option<u64>,
55 pub grid_carbon_intensity: Option<i16>,
56 pub grid_carbon_level: Option<ThreeLevel>,
57 pub local_carbon_intensity: Option<i16>,
58 pub local_carbon_level: Option<ThreeLevel>,
59}
60
61pub fn decode_local_generation_available(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<bool>> {
65 if let tlv::TlvItemValue::Bool(v) = inp {
66 Ok(Some(*v))
67 } else {
68 Ok(None)
69 }
70}
71
72pub fn decode_current_conditions(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<ElectricalGridConditions>> {
74 if let tlv::TlvItemValue::List(_fields) = inp {
75 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
77 Ok(Some(ElectricalGridConditions {
78 period_start: item.get_int(&[0]),
79 period_end: item.get_int(&[1]),
80 grid_carbon_intensity: item.get_int(&[2]).map(|v| v as i16),
81 grid_carbon_level: item.get_int(&[3]).and_then(|v| ThreeLevel::from_u8(v as u8)),
82 local_carbon_intensity: item.get_int(&[4]).map(|v| v as i16),
83 local_carbon_level: item.get_int(&[5]).and_then(|v| ThreeLevel::from_u8(v as u8)),
84 }))
85 } else {
89 Ok(None)
90 }
92}
93
94pub fn decode_forecast_conditions(inp: &tlv::TlvItemValue) -> anyhow::Result<Vec<ElectricalGridConditions>> {
96 let mut res = Vec::new();
97 if let tlv::TlvItemValue::List(v) = inp {
98 for item in v {
99 res.push(ElectricalGridConditions {
100 period_start: item.get_int(&[0]),
101 period_end: item.get_int(&[1]),
102 grid_carbon_intensity: item.get_int(&[2]).map(|v| v as i16),
103 grid_carbon_level: item.get_int(&[3]).and_then(|v| ThreeLevel::from_u8(v as u8)),
104 local_carbon_intensity: item.get_int(&[4]).map(|v| v as i16),
105 local_carbon_level: item.get_int(&[5]).and_then(|v| ThreeLevel::from_u8(v as u8)),
106 });
107 }
108 }
109 Ok(res)
110}
111
112
113pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
125 if cluster_id != 0x00A0 {
127 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x00A0, got {}\"}}", cluster_id);
128 }
129
130 match attribute_id {
131 0x0000 => {
132 match decode_local_generation_available(tlv_value) {
133 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
134 Err(e) => format!("{{\"error\": \"{}\"}}", e),
135 }
136 }
137 0x0001 => {
138 match decode_current_conditions(tlv_value) {
139 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
140 Err(e) => format!("{{\"error\": \"{}\"}}", e),
141 }
142 }
143 0x0002 => {
144 match decode_forecast_conditions(tlv_value) {
145 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
146 Err(e) => format!("{{\"error\": \"{}\"}}", e),
147 }
148 }
149 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
150 }
151}
152
153pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
158 vec![
159 (0x0000, "LocalGenerationAvailable"),
160 (0x0001, "CurrentConditions"),
161 (0x0002, "ForecastConditions"),
162 ]
163}
164
165pub async fn read_local_generation_available(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<bool>> {
169 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ELECTRICAL_GRID_CONDITIONS, crate::clusters::defs::CLUSTER_ELECTRICAL_GRID_CONDITIONS_ATTR_ID_LOCALGENERATIONAVAILABLE).await?;
170 decode_local_generation_available(&tlv)
171}
172
173pub async fn read_current_conditions(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Option<ElectricalGridConditions>> {
175 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ELECTRICAL_GRID_CONDITIONS, crate::clusters::defs::CLUSTER_ELECTRICAL_GRID_CONDITIONS_ATTR_ID_CURRENTCONDITIONS).await?;
176 decode_current_conditions(&tlv)
177}
178
179pub async fn read_forecast_conditions(conn: &crate::controller::Connection, endpoint: u16) -> anyhow::Result<Vec<ElectricalGridConditions>> {
181 let tlv = conn.read_request2(endpoint, crate::clusters::defs::CLUSTER_ID_ELECTRICAL_GRID_CONDITIONS, crate::clusters::defs::CLUSTER_ELECTRICAL_GRID_CONDITIONS_ATTR_ID_FORECASTCONDITIONS).await?;
182 decode_forecast_conditions(&tlv)
183}
184
185#[derive(Debug, serde::Serialize)]
186pub struct CurrentConditionsChangedEvent {
187 pub current_conditions: Option<ElectricalGridConditions>,
188}
189
190pub fn decode_current_conditions_changed_event(inp: &tlv::TlvItemValue) -> anyhow::Result<CurrentConditionsChangedEvent> {
194 if let tlv::TlvItemValue::List(_fields) = inp {
195 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
196 Ok(CurrentConditionsChangedEvent {
197 current_conditions: {
198 if let Some(nested_tlv) = item.get(&[0]) {
199 if let tlv::TlvItemValue::List(_) = nested_tlv {
200 let nested_item = tlv::TlvItem { tag: 0, value: nested_tlv.clone() };
201 Some(ElectricalGridConditions {
202 period_start: nested_item.get_int(&[0]),
203 period_end: nested_item.get_int(&[1]),
204 grid_carbon_intensity: nested_item.get_int(&[2]).map(|v| v as i16),
205 grid_carbon_level: nested_item.get_int(&[3]).and_then(|v| ThreeLevel::from_u8(v as u8)),
206 local_carbon_intensity: nested_item.get_int(&[4]).map(|v| v as i16),
207 local_carbon_level: nested_item.get_int(&[5]).and_then(|v| ThreeLevel::from_u8(v as u8)),
208 })
209 } else {
210 None
211 }
212 } else {
213 None
214 }
215 },
216 })
217 } else {
218 Err(anyhow::anyhow!("Expected struct fields"))
219 }
220}
221