matc/clusters/codec/
occupancy_sensing.rs1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, serde::Serialize)]
14pub struct HoldTimeLimits {
15 pub hold_time_min: Option<u16>,
16 pub hold_time_max: Option<u16>,
17 pub hold_time_default: Option<u16>,
18}
19
20pub fn decode_occupancy(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
24 if let tlv::TlvItemValue::Int(v) = inp {
25 Ok(*v as u8)
26 } else {
27 Err(anyhow::anyhow!("Expected Integer"))
28 }
29}
30
31pub fn decode_occupancy_sensor_type(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
33 if let tlv::TlvItemValue::Int(v) = inp {
34 Ok(*v as u8)
35 } else {
36 Err(anyhow::anyhow!("Expected Integer"))
37 }
38}
39
40pub fn decode_occupancy_sensor_type_bitmap(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
42 if let tlv::TlvItemValue::Int(v) = inp {
43 Ok(*v as u8)
44 } else {
45 Err(anyhow::anyhow!("Expected Integer"))
46 }
47}
48
49pub fn decode_hold_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
51 if let tlv::TlvItemValue::Int(v) = inp {
52 Ok(*v as u16)
53 } else {
54 Err(anyhow::anyhow!("Expected Integer"))
55 }
56}
57
58pub fn decode_hold_time_limits(inp: &tlv::TlvItemValue) -> anyhow::Result<HoldTimeLimits> {
60 if let tlv::TlvItemValue::List(_fields) = inp {
61 let item = tlv::TlvItem { tag: 0, value: inp.clone() };
63 Ok(HoldTimeLimits {
64 hold_time_min: item.get_int(&[0]).map(|v| v as u16),
65 hold_time_max: item.get_int(&[1]).map(|v| v as u16),
66 hold_time_default: item.get_int(&[2]).map(|v| v as u16),
67 })
68 } else {
69 Err(anyhow::anyhow!("Expected struct fields"))
70 }
71}
72
73pub fn decode_pir_occupied_to_unoccupied_delay(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
75 if let tlv::TlvItemValue::Int(v) = inp {
76 Ok(*v as u16)
77 } else {
78 Err(anyhow::anyhow!("Expected Integer"))
79 }
80}
81
82pub fn decode_pir_unoccupied_to_occupied_delay(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
84 if let tlv::TlvItemValue::Int(v) = inp {
85 Ok(*v as u16)
86 } else {
87 Err(anyhow::anyhow!("Expected Integer"))
88 }
89}
90
91pub fn decode_pir_unoccupied_to_occupied_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
93 if let tlv::TlvItemValue::Int(v) = inp {
94 Ok(*v as u8)
95 } else {
96 Err(anyhow::anyhow!("Expected Integer"))
97 }
98}
99
100pub fn decode_ultrasonic_occupied_to_unoccupied_delay(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
102 if let tlv::TlvItemValue::Int(v) = inp {
103 Ok(*v as u16)
104 } else {
105 Err(anyhow::anyhow!("Expected Integer"))
106 }
107}
108
109pub fn decode_ultrasonic_unoccupied_to_occupied_delay(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
111 if let tlv::TlvItemValue::Int(v) = inp {
112 Ok(*v as u16)
113 } else {
114 Err(anyhow::anyhow!("Expected Integer"))
115 }
116}
117
118pub fn decode_ultrasonic_unoccupied_to_occupied_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
120 if let tlv::TlvItemValue::Int(v) = inp {
121 Ok(*v as u8)
122 } else {
123 Err(anyhow::anyhow!("Expected Integer"))
124 }
125}
126
127pub fn decode_physical_contact_occupied_to_unoccupied_delay(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
129 if let tlv::TlvItemValue::Int(v) = inp {
130 Ok(*v as u16)
131 } else {
132 Err(anyhow::anyhow!("Expected Integer"))
133 }
134}
135
136pub fn decode_physical_contact_unoccupied_to_occupied_delay(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
138 if let tlv::TlvItemValue::Int(v) = inp {
139 Ok(*v as u16)
140 } else {
141 Err(anyhow::anyhow!("Expected Integer"))
142 }
143}
144
145pub fn decode_physical_contact_unoccupied_to_occupied_threshold(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
147 if let tlv::TlvItemValue::Int(v) = inp {
148 Ok(*v as u8)
149 } else {
150 Err(anyhow::anyhow!("Expected Integer"))
151 }
152}
153
154
155pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
167 if cluster_id != 0x0406 {
169 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0406, got {}\"}}", cluster_id);
170 }
171
172 match attribute_id {
173 0x0000 => {
174 match decode_occupancy(tlv_value) {
175 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
176 Err(e) => format!("{{\"error\": \"{}\"}}", e),
177 }
178 }
179 0x0001 => {
180 match decode_occupancy_sensor_type(tlv_value) {
181 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
182 Err(e) => format!("{{\"error\": \"{}\"}}", e),
183 }
184 }
185 0x0002 => {
186 match decode_occupancy_sensor_type_bitmap(tlv_value) {
187 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
188 Err(e) => format!("{{\"error\": \"{}\"}}", e),
189 }
190 }
191 0x0003 => {
192 match decode_hold_time(tlv_value) {
193 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
194 Err(e) => format!("{{\"error\": \"{}\"}}", e),
195 }
196 }
197 0x0004 => {
198 match decode_hold_time_limits(tlv_value) {
199 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
200 Err(e) => format!("{{\"error\": \"{}\"}}", e),
201 }
202 }
203 0x0010 => {
204 match decode_pir_occupied_to_unoccupied_delay(tlv_value) {
205 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
206 Err(e) => format!("{{\"error\": \"{}\"}}", e),
207 }
208 }
209 0x0011 => {
210 match decode_pir_unoccupied_to_occupied_delay(tlv_value) {
211 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
212 Err(e) => format!("{{\"error\": \"{}\"}}", e),
213 }
214 }
215 0x0012 => {
216 match decode_pir_unoccupied_to_occupied_threshold(tlv_value) {
217 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
218 Err(e) => format!("{{\"error\": \"{}\"}}", e),
219 }
220 }
221 0x0020 => {
222 match decode_ultrasonic_occupied_to_unoccupied_delay(tlv_value) {
223 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
224 Err(e) => format!("{{\"error\": \"{}\"}}", e),
225 }
226 }
227 0x0021 => {
228 match decode_ultrasonic_unoccupied_to_occupied_delay(tlv_value) {
229 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
230 Err(e) => format!("{{\"error\": \"{}\"}}", e),
231 }
232 }
233 0x0022 => {
234 match decode_ultrasonic_unoccupied_to_occupied_threshold(tlv_value) {
235 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
236 Err(e) => format!("{{\"error\": \"{}\"}}", e),
237 }
238 }
239 0x0030 => {
240 match decode_physical_contact_occupied_to_unoccupied_delay(tlv_value) {
241 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
242 Err(e) => format!("{{\"error\": \"{}\"}}", e),
243 }
244 }
245 0x0031 => {
246 match decode_physical_contact_unoccupied_to_occupied_delay(tlv_value) {
247 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
248 Err(e) => format!("{{\"error\": \"{}\"}}", e),
249 }
250 }
251 0x0032 => {
252 match decode_physical_contact_unoccupied_to_occupied_threshold(tlv_value) {
253 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
254 Err(e) => format!("{{\"error\": \"{}\"}}", e),
255 }
256 }
257 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
258 }
259}
260
261pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
266 vec![
267 (0x0000, "Occupancy"),
268 (0x0001, "OccupancySensorType"),
269 (0x0002, "OccupancySensorTypeBitmap"),
270 (0x0003, "HoldTime"),
271 (0x0004, "HoldTimeLimits"),
272 (0x0010, "PIROccupiedToUnoccupiedDelay"),
273 (0x0011, "PIRUnoccupiedToOccupiedDelay"),
274 (0x0012, "PIRUnoccupiedToOccupiedThreshold"),
275 (0x0020, "UltrasonicOccupiedToUnoccupiedDelay"),
276 (0x0021, "UltrasonicUnoccupiedToOccupiedDelay"),
277 (0x0022, "UltrasonicUnoccupiedToOccupiedThreshold"),
278 (0x0030, "PhysicalContactOccupiedToUnoccupiedDelay"),
279 (0x0031, "PhysicalContactUnoccupiedToOccupiedDelay"),
280 (0x0032, "PhysicalContactUnoccupiedToOccupiedThreshold"),
281 ]
282}
283