1use crate::tlv;
7use anyhow;
8use serde_json;
9
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
14#[repr(u8)]
15pub enum MoveMode {
16 Up = 0,
18 Down = 1,
20}
21
22impl MoveMode {
23 pub fn from_u8(value: u8) -> Option<Self> {
25 match value {
26 0 => Some(MoveMode::Up),
27 1 => Some(MoveMode::Down),
28 _ => None,
29 }
30 }
31
32 pub fn to_u8(self) -> u8 {
34 self as u8
35 }
36}
37
38impl From<MoveMode> for u8 {
39 fn from(val: MoveMode) -> Self {
40 val as u8
41 }
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
45#[repr(u8)]
46pub enum StepMode {
47 Up = 0,
49 Down = 1,
51}
52
53impl StepMode {
54 pub fn from_u8(value: u8) -> Option<Self> {
56 match value {
57 0 => Some(StepMode::Up),
58 1 => Some(StepMode::Down),
59 _ => None,
60 }
61 }
62
63 pub fn to_u8(self) -> u8 {
65 self as u8
66 }
67}
68
69impl From<StepMode> for u8 {
70 fn from(val: StepMode) -> Self {
71 val as u8
72 }
73}
74
75pub type Options = u8;
79
80pub mod options {
82 pub const EXECUTE_IF_OFF: u8 = 0x01;
84 pub const COUPLE_COLOR_TEMP_TO_LEVEL: u8 = 0x02;
86}
87
88pub fn encode_move_to_level(level: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
92 let tlv = tlv::TlvItemEnc {
93 tag: 0,
94 value: tlv::TlvItemValueEnc::StructInvisible(vec![
95 (0, tlv::TlvItemValueEnc::UInt8(level)).into(),
96 (1, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
97 (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
98 (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
99 ]),
100 };
101 Ok(tlv.encode()?)
102}
103
104pub fn encode_move_(move_mode: MoveMode, rate: Option<u8>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
106 let tlv = tlv::TlvItemEnc {
107 tag: 0,
108 value: tlv::TlvItemValueEnc::StructInvisible(vec![
109 (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
110 (1, tlv::TlvItemValueEnc::UInt8(rate.unwrap_or(0))).into(),
111 (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
112 (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
113 ]),
114 };
115 Ok(tlv.encode()?)
116}
117
118pub fn encode_step(step_mode: StepMode, step_size: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
120 let tlv = tlv::TlvItemEnc {
121 tag: 0,
122 value: tlv::TlvItemValueEnc::StructInvisible(vec![
123 (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
124 (1, tlv::TlvItemValueEnc::UInt8(step_size)).into(),
125 (2, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
126 (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
127 (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
128 ]),
129 };
130 Ok(tlv.encode()?)
131}
132
133pub fn encode_stop(options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
135 let tlv = tlv::TlvItemEnc {
136 tag: 0,
137 value: tlv::TlvItemValueEnc::StructInvisible(vec![
138 (0, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
139 (1, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
140 ]),
141 };
142 Ok(tlv.encode()?)
143}
144
145pub fn encode_move_to_level_with_on_off(level: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
147 let tlv = tlv::TlvItemEnc {
148 tag: 0,
149 value: tlv::TlvItemValueEnc::StructInvisible(vec![
150 (0, tlv::TlvItemValueEnc::UInt8(level)).into(),
151 (1, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
152 (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
153 (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
154 ]),
155 };
156 Ok(tlv.encode()?)
157}
158
159pub fn encode_move_with_on_off(move_mode: MoveMode, rate: Option<u8>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
161 let tlv = tlv::TlvItemEnc {
162 tag: 0,
163 value: tlv::TlvItemValueEnc::StructInvisible(vec![
164 (0, tlv::TlvItemValueEnc::UInt8(move_mode.to_u8())).into(),
165 (1, tlv::TlvItemValueEnc::UInt8(rate.unwrap_or(0))).into(),
166 (2, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
167 (3, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
168 ]),
169 };
170 Ok(tlv.encode()?)
171}
172
173pub fn encode_step_with_on_off(step_mode: StepMode, step_size: u8, transition_time: Option<u16>, options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
175 let tlv = tlv::TlvItemEnc {
176 tag: 0,
177 value: tlv::TlvItemValueEnc::StructInvisible(vec![
178 (0, tlv::TlvItemValueEnc::UInt8(step_mode.to_u8())).into(),
179 (1, tlv::TlvItemValueEnc::UInt8(step_size)).into(),
180 (2, tlv::TlvItemValueEnc::UInt16(transition_time.unwrap_or(0))).into(),
181 (3, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
182 (4, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
183 ]),
184 };
185 Ok(tlv.encode()?)
186}
187
188pub fn encode_stop_with_on_off(options_mask: Options, options_override: Options) -> anyhow::Result<Vec<u8>> {
190 let tlv = tlv::TlvItemEnc {
191 tag: 0,
192 value: tlv::TlvItemValueEnc::StructInvisible(vec![
193 (0, tlv::TlvItemValueEnc::UInt8(options_mask)).into(),
194 (1, tlv::TlvItemValueEnc::UInt8(options_override)).into(),
195 ]),
196 };
197 Ok(tlv.encode()?)
198}
199
200pub fn encode_move_to_closest_frequency(frequency: u16) -> anyhow::Result<Vec<u8>> {
202 let tlv = tlv::TlvItemEnc {
203 tag: 0,
204 value: tlv::TlvItemValueEnc::StructInvisible(vec![
205 (0, tlv::TlvItemValueEnc::UInt16(frequency)).into(),
206 ]),
207 };
208 Ok(tlv.encode()?)
209}
210
211pub fn decode_current_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
215 if let tlv::TlvItemValue::Int(v) = inp {
216 Ok(Some(*v as u8))
217 } else {
218 Ok(None)
219 }
220}
221
222pub fn decode_remaining_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
224 if let tlv::TlvItemValue::Int(v) = inp {
225 Ok(*v as u16)
226 } else {
227 Err(anyhow::anyhow!("Expected UInt16"))
228 }
229}
230
231pub fn decode_min_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
233 if let tlv::TlvItemValue::Int(v) = inp {
234 Ok(*v as u8)
235 } else {
236 Err(anyhow::anyhow!("Expected UInt8"))
237 }
238}
239
240pub fn decode_max_level(inp: &tlv::TlvItemValue) -> anyhow::Result<u8> {
242 if let tlv::TlvItemValue::Int(v) = inp {
243 Ok(*v as u8)
244 } else {
245 Err(anyhow::anyhow!("Expected UInt8"))
246 }
247}
248
249pub fn decode_current_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
251 if let tlv::TlvItemValue::Int(v) = inp {
252 Ok(*v as u16)
253 } else {
254 Err(anyhow::anyhow!("Expected UInt16"))
255 }
256}
257
258pub fn decode_min_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
260 if let tlv::TlvItemValue::Int(v) = inp {
261 Ok(*v as u16)
262 } else {
263 Err(anyhow::anyhow!("Expected UInt16"))
264 }
265}
266
267pub fn decode_max_frequency(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
269 if let tlv::TlvItemValue::Int(v) = inp {
270 Ok(*v as u16)
271 } else {
272 Err(anyhow::anyhow!("Expected UInt16"))
273 }
274}
275
276pub fn decode_options(inp: &tlv::TlvItemValue) -> anyhow::Result<Options> {
278 if let tlv::TlvItemValue::Int(v) = inp {
279 Ok(*v as u8)
280 } else {
281 Err(anyhow::anyhow!("Expected Integer"))
282 }
283}
284
285pub fn decode_on_off_transition_time(inp: &tlv::TlvItemValue) -> anyhow::Result<u16> {
287 if let tlv::TlvItemValue::Int(v) = inp {
288 Ok(*v as u16)
289 } else {
290 Err(anyhow::anyhow!("Expected UInt16"))
291 }
292}
293
294pub fn decode_on_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
296 if let tlv::TlvItemValue::Int(v) = inp {
297 Ok(Some(*v as u8))
298 } else {
299 Ok(None)
300 }
301}
302
303pub fn decode_on_transition_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
305 if let tlv::TlvItemValue::Int(v) = inp {
306 Ok(Some(*v as u16))
307 } else {
308 Ok(None)
309 }
310}
311
312pub fn decode_off_transition_time(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u16>> {
314 if let tlv::TlvItemValue::Int(v) = inp {
315 Ok(Some(*v as u16))
316 } else {
317 Ok(None)
318 }
319}
320
321pub fn decode_default_move_rate(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
323 if let tlv::TlvItemValue::Int(v) = inp {
324 Ok(Some(*v as u8))
325 } else {
326 Ok(None)
327 }
328}
329
330pub fn decode_start_up_current_level(inp: &tlv::TlvItemValue) -> anyhow::Result<Option<u8>> {
332 if let tlv::TlvItemValue::Int(v) = inp {
333 Ok(Some(*v as u8))
334 } else {
335 Ok(None)
336 }
337}
338
339
340pub fn decode_attribute_json(cluster_id: u32, attribute_id: u32, tlv_value: &crate::tlv::TlvItemValue) -> String {
352 if cluster_id != 0x0008 {
354 return format!("{{\"error\": \"Invalid cluster ID. Expected 0x0008, got {}\"}}", cluster_id);
355 }
356
357 match attribute_id {
358 0x0000 => {
359 match decode_current_level(tlv_value) {
360 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
361 Err(e) => format!("{{\"error\": \"{}\"}}", e),
362 }
363 }
364 0x0001 => {
365 match decode_remaining_time(tlv_value) {
366 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
367 Err(e) => format!("{{\"error\": \"{}\"}}", e),
368 }
369 }
370 0x0002 => {
371 match decode_min_level(tlv_value) {
372 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
373 Err(e) => format!("{{\"error\": \"{}\"}}", e),
374 }
375 }
376 0x0003 => {
377 match decode_max_level(tlv_value) {
378 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
379 Err(e) => format!("{{\"error\": \"{}\"}}", e),
380 }
381 }
382 0x0004 => {
383 match decode_current_frequency(tlv_value) {
384 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
385 Err(e) => format!("{{\"error\": \"{}\"}}", e),
386 }
387 }
388 0x0005 => {
389 match decode_min_frequency(tlv_value) {
390 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
391 Err(e) => format!("{{\"error\": \"{}\"}}", e),
392 }
393 }
394 0x0006 => {
395 match decode_max_frequency(tlv_value) {
396 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
397 Err(e) => format!("{{\"error\": \"{}\"}}", e),
398 }
399 }
400 0x000F => {
401 match decode_options(tlv_value) {
402 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
403 Err(e) => format!("{{\"error\": \"{}\"}}", e),
404 }
405 }
406 0x0010 => {
407 match decode_on_off_transition_time(tlv_value) {
408 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
409 Err(e) => format!("{{\"error\": \"{}\"}}", e),
410 }
411 }
412 0x0011 => {
413 match decode_on_level(tlv_value) {
414 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
415 Err(e) => format!("{{\"error\": \"{}\"}}", e),
416 }
417 }
418 0x0012 => {
419 match decode_on_transition_time(tlv_value) {
420 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
421 Err(e) => format!("{{\"error\": \"{}\"}}", e),
422 }
423 }
424 0x0013 => {
425 match decode_off_transition_time(tlv_value) {
426 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
427 Err(e) => format!("{{\"error\": \"{}\"}}", e),
428 }
429 }
430 0x0014 => {
431 match decode_default_move_rate(tlv_value) {
432 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
433 Err(e) => format!("{{\"error\": \"{}\"}}", e),
434 }
435 }
436 0x4000 => {
437 match decode_start_up_current_level(tlv_value) {
438 Ok(value) => serde_json::to_string(&value).unwrap_or_else(|_| "null".to_string()),
439 Err(e) => format!("{{\"error\": \"{}\"}}", e),
440 }
441 }
442 _ => format!("{{\"error\": \"Unknown attribute ID: {}\"}}", attribute_id),
443 }
444}
445
446pub fn get_attribute_list() -> Vec<(u32, &'static str)> {
451 vec![
452 (0x0000, "CurrentLevel"),
453 (0x0001, "RemainingTime"),
454 (0x0002, "MinLevel"),
455 (0x0003, "MaxLevel"),
456 (0x0004, "CurrentFrequency"),
457 (0x0005, "MinFrequency"),
458 (0x0006, "MaxFrequency"),
459 (0x000F, "Options"),
460 (0x0010, "OnOffTransitionTime"),
461 (0x0011, "OnLevel"),
462 (0x0012, "OnTransitionTime"),
463 (0x0013, "OffTransitionTime"),
464 (0x0014, "DefaultMoveRate"),
465 (0x4000, "StartUpCurrentLevel"),
466 ]
467}
468