1pub mod builder;
32
33pub use builder::ZWaveBuilder;
34
35use crate::layer::field::{FieldError, FieldValue};
36use crate::layer::{Layer, LayerIndex, LayerKind};
37
38pub const ZWAVE_MIN_HEADER_LEN: usize = 10;
40
41pub const ZWAVE_HEADER_LEN: usize = 10;
43
44pub mod cmd_class {
46 pub const NO_OPERATION: u8 = 0x00;
47 pub const BASIC: u8 = 0x20;
48 pub const CONTROLLER_REPLICATION: u8 = 0x21;
49 pub const APPLICATION_STATUS: u8 = 0x22;
50 pub const ZIP_SERVICES: u8 = 0x23;
51 pub const ZIP_SERVER: u8 = 0x24;
52 pub const SWITCH_BINARY: u8 = 0x25;
53 pub const SWITCH_MULTILEVEL: u8 = 0x26;
54 pub const SWITCH_ALL: u8 = 0x27;
55 pub const SWITCH_TOGGLE_BINARY: u8 = 0x28;
56 pub const SWITCH_TOGGLE_MULTILEVEL: u8 = 0x29;
57 pub const CHIMNEY_FAN: u8 = 0x2A;
58 pub const SCENE_ACTIVATION: u8 = 0x2B;
59 pub const SCENE_ACTUATOR_CONF: u8 = 0x2C;
60 pub const SCENE_CONTROLLER_CONF: u8 = 0x2D;
61 pub const ZIP_CLIENT: u8 = 0x2E;
62 pub const ZIP_ADV_SERVICES: u8 = 0x2F;
63 pub const SENSOR_BINARY: u8 = 0x30;
64 pub const SENSOR_MULTILEVEL: u8 = 0x31;
65 pub const METER: u8 = 0x32;
66 pub const ZIP_ADV_SERVER: u8 = 0x33;
67 pub const ZIP_ADV_CLIENT: u8 = 0x34;
68 pub const METER_PULSE: u8 = 0x35;
69 pub const THERMOSTAT_HEATING: u8 = 0x38;
70 pub const METER_TBL_CONFIG: u8 = 0x3C;
71 pub const METER_TBL_MONITOR: u8 = 0x3D;
72 pub const METER_TBL_PUSH: u8 = 0x3E;
73 pub const THERMOSTAT_MODE: u8 = 0x40;
74 pub const THERMOSTAT_OPERATING_STATE: u8 = 0x42;
75 pub const THERMOSTAT_SETPOINT: u8 = 0x43;
76 pub const THERMOSTAT_FAN_MODE: u8 = 0x44;
77 pub const THERMOSTAT_FAN_STATE: u8 = 0x45;
78 pub const CLIMATE_CONTROL_SCHEDULE: u8 = 0x46;
79 pub const THERMOSTAT_SETBACK: u8 = 0x47;
80 pub const DOOR_LOCK_LOGGING: u8 = 0x4C;
81 pub const SCHEDULE_ENTRY_LOCK: u8 = 0x4E;
82 pub const BASIC_WINDOW_COVERING: u8 = 0x50;
83 pub const MTP_WINDOW_COVERING: u8 = 0x51;
84 pub const MULTI_CHANNEL_V2: u8 = 0x60;
85 pub const MULTI_INSTANCE: u8 = 0x61;
86 pub const DOOR_LOCK: u8 = 0x62;
87 pub const USER_CODE: u8 = 0x63;
88 pub const CONFIGURATION: u8 = 0x70;
89 pub const ALARM: u8 = 0x71;
90 pub const MANUFACTURER_SPECIFIC: u8 = 0x72;
91 pub const POWERLEVEL: u8 = 0x73;
92 pub const PROTECTION: u8 = 0x75;
93 pub const LOCK: u8 = 0x76;
94 pub const NODE_NAMING: u8 = 0x77;
95 pub const FIRMWARE_UPDATE_MD: u8 = 0x7A;
96 pub const GROUPING_NAME: u8 = 0x7B;
97 pub const REMOTE_ASSOCIATION_ACTIVATE: u8 = 0x7C;
98 pub const REMOTE_ASSOCIATION: u8 = 0x7D;
99 pub const BATTERY: u8 = 0x80;
100 pub const CLOCK: u8 = 0x81;
101 pub const HAIL: u8 = 0x82;
102 pub const WAKE_UP: u8 = 0x84;
103 pub const ASSOCIATION: u8 = 0x85;
104 pub const VERSION: u8 = 0x86;
105 pub const INDICATOR: u8 = 0x87;
106 pub const PROPRIETARY: u8 = 0x88;
107 pub const LANGUAGE: u8 = 0x89;
108 pub const TIME: u8 = 0x8A;
109 pub const TIME_PARAMETERS: u8 = 0x8B;
110 pub const GEOGRAPHIC_LOCATION: u8 = 0x8C;
111 pub const COMPOSITE: u8 = 0x8D;
112 pub const MULTI_INSTANCE_ASSOCIATION: u8 = 0x8E;
113 pub const MULTI_CMD: u8 = 0x8F;
114 pub const ENERGY_PRODUCTION: u8 = 0x90;
115 pub const MANUFACTURER_PROPRIETARY: u8 = 0x91;
116 pub const SCREEN_MD: u8 = 0x92;
117 pub const SCREEN_ATTRIBUTES: u8 = 0x93;
118 pub const SIMPLE_AV_CONTROL: u8 = 0x94;
119 pub const AV_CONTENT_DIRECTORY_MD: u8 = 0x95;
120 pub const AV_RENDERER_STATUS: u8 = 0x96;
121 pub const AV_CONTENT_SEARCH_MD: u8 = 0x97;
122 pub const SECURITY: u8 = 0x98;
123 pub const AV_TAGGING_MD: u8 = 0x99;
124 pub const SIP_CONFIGURATION: u8 = 0x9A;
125 pub const ASSOCIATION_COMMAND_CONFIGURATION: u8 = 0x9B;
126 pub const SENSOR_ALARM: u8 = 0x9C;
127 pub const SILENCE_ALARM: u8 = 0x9D;
128 pub const MARK: u8 = 0x9E;
129 pub const NON_INTEROPERABLE: u8 = 0xF0;
130}
131
132#[must_use]
134pub fn cmd_class_name(cc: u8) -> &'static str {
135 match cc {
136 cmd_class::NO_OPERATION => "NO_OPERATION",
137 cmd_class::BASIC => "BASIC",
138 cmd_class::CONTROLLER_REPLICATION => "CONTROLLER_REPLICATION",
139 cmd_class::APPLICATION_STATUS => "APPLICATION_STATUS",
140 cmd_class::ZIP_SERVICES => "ZIP_SERVICES",
141 cmd_class::ZIP_SERVER => "ZIP_SERVER",
142 cmd_class::SWITCH_BINARY => "SWITCH_BINARY",
143 cmd_class::SWITCH_MULTILEVEL => "SWITCH_MULTILEVEL",
144 cmd_class::SWITCH_ALL => "SWITCH_ALL",
145 cmd_class::SWITCH_TOGGLE_BINARY => "SWITCH_TOGGLE_BINARY",
146 cmd_class::SWITCH_TOGGLE_MULTILEVEL => "SWITCH_TOGGLE_MULTILEVEL",
147 cmd_class::CHIMNEY_FAN => "CHIMNEY_FAN",
148 cmd_class::SCENE_ACTIVATION => "SCENE_ACTIVATION",
149 cmd_class::SCENE_ACTUATOR_CONF => "SCENE_ACTUATOR_CONF",
150 cmd_class::SCENE_CONTROLLER_CONF => "SCENE_CONTROLLER_CONF",
151 cmd_class::ZIP_CLIENT => "ZIP_CLIENT",
152 cmd_class::ZIP_ADV_SERVICES => "ZIP_ADV_SERVICES",
153 cmd_class::SENSOR_BINARY => "SENSOR_BINARY",
154 cmd_class::SENSOR_MULTILEVEL => "SENSOR_MULTILEVEL",
155 cmd_class::METER => "METER",
156 cmd_class::ZIP_ADV_SERVER => "ZIP_ADV_SERVER",
157 cmd_class::ZIP_ADV_CLIENT => "ZIP_ADV_CLIENT",
158 cmd_class::METER_PULSE => "METER_PULSE",
159 cmd_class::THERMOSTAT_HEATING => "THERMOSTAT_HEATING",
160 cmd_class::METER_TBL_CONFIG => "METER_TBL_CONFIG",
161 cmd_class::METER_TBL_MONITOR => "METER_TBL_MONITOR",
162 cmd_class::METER_TBL_PUSH => "METER_TBL_PUSH",
163 cmd_class::THERMOSTAT_MODE => "THERMOSTAT_MODE",
164 cmd_class::THERMOSTAT_OPERATING_STATE => "THERMOSTAT_OPERATING_STATE",
165 cmd_class::THERMOSTAT_SETPOINT => "THERMOSTAT_SETPOINT",
166 cmd_class::THERMOSTAT_FAN_MODE => "THERMOSTAT_FAN_MODE",
167 cmd_class::THERMOSTAT_FAN_STATE => "THERMOSTAT_FAN_STATE",
168 cmd_class::CLIMATE_CONTROL_SCHEDULE => "CLIMATE_CONTROL_SCHEDULE",
169 cmd_class::THERMOSTAT_SETBACK => "THERMOSTAT_SETBACK",
170 cmd_class::DOOR_LOCK_LOGGING => "DOOR_LOCK_LOGGING",
171 cmd_class::SCHEDULE_ENTRY_LOCK => "SCHEDULE_ENTRY_LOCK",
172 cmd_class::BASIC_WINDOW_COVERING => "BASIC_WINDOW_COVERING",
173 cmd_class::MTP_WINDOW_COVERING => "MTP_WINDOW_COVERING",
174 cmd_class::MULTI_CHANNEL_V2 => "MULTI_CHANNEL_V2",
175 cmd_class::MULTI_INSTANCE => "MULTI_INSTANCE",
176 cmd_class::DOOR_LOCK => "DOOR_LOCK",
177 cmd_class::USER_CODE => "USER_CODE",
178 cmd_class::CONFIGURATION => "CONFIGURATION",
179 cmd_class::ALARM => "ALARM",
180 cmd_class::MANUFACTURER_SPECIFIC => "MANUFACTURER_SPECIFIC",
181 cmd_class::POWERLEVEL => "POWERLEVEL",
182 cmd_class::PROTECTION => "PROTECTION",
183 cmd_class::LOCK => "LOCK",
184 cmd_class::NODE_NAMING => "NODE_NAMING",
185 cmd_class::FIRMWARE_UPDATE_MD => "FIRMWARE_UPDATE_MD",
186 cmd_class::GROUPING_NAME => "GROUPING_NAME",
187 cmd_class::REMOTE_ASSOCIATION_ACTIVATE => "REMOTE_ASSOCIATION_ACTIVATE",
188 cmd_class::REMOTE_ASSOCIATION => "REMOTE_ASSOCIATION",
189 cmd_class::BATTERY => "BATTERY",
190 cmd_class::CLOCK => "CLOCK",
191 cmd_class::HAIL => "HAIL",
192 cmd_class::WAKE_UP => "WAKE_UP",
193 cmd_class::ASSOCIATION => "ASSOCIATION",
194 cmd_class::VERSION => "VERSION",
195 cmd_class::INDICATOR => "INDICATOR",
196 cmd_class::PROPRIETARY => "PROPRIETARY",
197 cmd_class::LANGUAGE => "LANGUAGE",
198 cmd_class::TIME => "TIME",
199 cmd_class::TIME_PARAMETERS => "TIME_PARAMETERS",
200 cmd_class::GEOGRAPHIC_LOCATION => "GEOGRAPHIC_LOCATION",
201 cmd_class::COMPOSITE => "COMPOSITE",
202 cmd_class::MULTI_INSTANCE_ASSOCIATION => "MULTI_INSTANCE_ASSOCIATION",
203 cmd_class::MULTI_CMD => "MULTI_CMD",
204 cmd_class::ENERGY_PRODUCTION => "ENERGY_PRODUCTION",
205 cmd_class::MANUFACTURER_PROPRIETARY => "MANUFACTURER_PROPRIETARY",
206 cmd_class::SCREEN_MD => "SCREEN_MD",
207 cmd_class::SCREEN_ATTRIBUTES => "SCREEN_ATTRIBUTES",
208 cmd_class::SIMPLE_AV_CONTROL => "SIMPLE_AV_CONTROL",
209 cmd_class::AV_CONTENT_DIRECTORY_MD => "AV_CONTENT_DIRECTORY_MD",
210 cmd_class::AV_RENDERER_STATUS => "AV_RENDERER_STATUS",
211 cmd_class::AV_CONTENT_SEARCH_MD => "AV_CONTENT_SEARCH_MD",
212 cmd_class::SECURITY => "SECURITY",
213 cmd_class::AV_TAGGING_MD => "AV_TAGGING_MD",
214 cmd_class::SIP_CONFIGURATION => "SIP_CONFIGURATION",
215 cmd_class::ASSOCIATION_COMMAND_CONFIGURATION => "ASSOCIATION_COMMAND_CONFIGURATION",
216 cmd_class::SENSOR_ALARM => "SENSOR_ALARM",
217 cmd_class::SILENCE_ALARM => "SILENCE_ALARM",
218 cmd_class::MARK => "MARK",
219 cmd_class::NON_INTEROPERABLE => "NON_INTEROPERABLE",
220 _ => "UNKNOWN",
221 }
222}
223
224pub static ZWAVE_FIELD_NAMES: &[&str] = &[
226 "home_id",
227 "src",
228 "dst",
229 "routed",
230 "ackreq",
231 "lowpower",
232 "speedmodified",
233 "headertype",
234 "beam_control",
235 "seqn",
236 "length",
237 "cmd_class",
238 "cmd",
239 "crc",
240];
241
242#[must_use]
244pub fn zwave_crc(data: &[u8]) -> u8 {
245 data.iter().fold(0xFFu8, |acc, &b| acc ^ b)
246}
247
248#[must_use]
253pub fn is_zwave_frame(buf: &[u8]) -> bool {
254 if buf.len() < 10 {
255 return false;
256 }
257 let length = buf[7] as usize;
258 length >= 10 && length <= buf.len()
259}
260
261#[derive(Debug, Clone)]
263pub struct ZWaveLayer {
264 pub index: LayerIndex,
265}
266
267impl ZWaveLayer {
268 #[must_use]
270 pub fn new(index: LayerIndex) -> Self {
271 Self { index }
272 }
273
274 #[must_use]
276 pub fn at_start(end: usize) -> Self {
277 Self {
278 index: LayerIndex::new(LayerKind::ZWave, 0, end),
279 }
280 }
281
282 fn slice<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
284 self.index.slice(buf)
285 }
286
287 pub fn home_id(&self, buf: &[u8]) -> Result<u32, FieldError> {
293 let s = self.slice(buf);
294 if s.len() < 4 {
295 return Err(FieldError::BufferTooShort {
296 offset: self.index.start,
297 need: 4,
298 have: s.len(),
299 });
300 }
301 Ok(u32::from_be_bytes([s[0], s[1], s[2], s[3]]))
302 }
303
304 pub fn set_home_id(&self, buf: &mut [u8], value: u32) -> Result<(), FieldError> {
306 let off = self.index.start;
307 if buf.len() < off + 4 {
308 return Err(FieldError::BufferTooShort {
309 offset: off,
310 need: 4,
311 have: buf.len().saturating_sub(off),
312 });
313 }
314 buf[off..off + 4].copy_from_slice(&value.to_be_bytes());
315 Ok(())
316 }
317
318 pub fn src(&self, buf: &[u8]) -> Result<u8, FieldError> {
320 let s = self.slice(buf);
321 if s.len() < 5 {
322 return Err(FieldError::BufferTooShort {
323 offset: self.index.start + 4,
324 need: 1,
325 have: s.len().saturating_sub(4),
326 });
327 }
328 Ok(s[4])
329 }
330
331 pub fn set_src(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
333 let off = self.index.start + 4;
334 if buf.len() <= off {
335 return Err(FieldError::BufferTooShort {
336 offset: off,
337 need: 1,
338 have: buf.len().saturating_sub(off),
339 });
340 }
341 buf[off] = value;
342 Ok(())
343 }
344
345 pub fn frame_ctrl(&self, buf: &[u8]) -> Result<u8, FieldError> {
347 let s = self.slice(buf);
348 if s.len() < 6 {
349 return Err(FieldError::BufferTooShort {
350 offset: self.index.start + 5,
351 need: 1,
352 have: s.len().saturating_sub(5),
353 });
354 }
355 Ok(s[5])
356 }
357
358 pub fn set_frame_ctrl(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
360 let off = self.index.start + 5;
361 if buf.len() <= off {
362 return Err(FieldError::BufferTooShort {
363 offset: off,
364 need: 1,
365 have: buf.len().saturating_sub(off),
366 });
367 }
368 buf[off] = value;
369 Ok(())
370 }
371
372 pub fn routed(&self, buf: &[u8]) -> Result<bool, FieldError> {
374 let fc = self.frame_ctrl(buf)?;
375 Ok((fc >> 7) & 0x01 == 1)
376 }
377
378 pub fn set_routed(&self, buf: &mut [u8], value: bool) -> Result<(), FieldError> {
380 let fc = self.frame_ctrl(buf)?;
381 let fc = if value { fc | 0x80 } else { fc & !0x80 };
382 self.set_frame_ctrl(buf, fc)
383 }
384
385 pub fn ackreq(&self, buf: &[u8]) -> Result<bool, FieldError> {
387 let fc = self.frame_ctrl(buf)?;
388 Ok((fc >> 6) & 0x01 == 1)
389 }
390
391 pub fn set_ackreq(&self, buf: &mut [u8], value: bool) -> Result<(), FieldError> {
393 let fc = self.frame_ctrl(buf)?;
394 let fc = if value { fc | 0x40 } else { fc & !0x40 };
395 self.set_frame_ctrl(buf, fc)
396 }
397
398 pub fn lowpower(&self, buf: &[u8]) -> Result<bool, FieldError> {
400 let fc = self.frame_ctrl(buf)?;
401 Ok((fc >> 5) & 0x01 == 1)
402 }
403
404 pub fn set_lowpower(&self, buf: &mut [u8], value: bool) -> Result<(), FieldError> {
406 let fc = self.frame_ctrl(buf)?;
407 let fc = if value { fc | 0x20 } else { fc & !0x20 };
408 self.set_frame_ctrl(buf, fc)
409 }
410
411 pub fn speedmodified(&self, buf: &[u8]) -> Result<bool, FieldError> {
413 let fc = self.frame_ctrl(buf)?;
414 Ok((fc >> 4) & 0x01 == 1)
415 }
416
417 pub fn set_speedmodified(&self, buf: &mut [u8], value: bool) -> Result<(), FieldError> {
419 let fc = self.frame_ctrl(buf)?;
420 let fc = if value { fc | 0x10 } else { fc & !0x10 };
421 self.set_frame_ctrl(buf, fc)
422 }
423
424 pub fn headertype(&self, buf: &[u8]) -> Result<u8, FieldError> {
426 let fc = self.frame_ctrl(buf)?;
427 Ok(fc & 0x0F)
428 }
429
430 pub fn set_headertype(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
432 let fc = self.frame_ctrl(buf)?;
433 let fc = (fc & 0xF0) | (value & 0x0F);
434 self.set_frame_ctrl(buf, fc)
435 }
436
437 pub fn beam_seqn(&self, buf: &[u8]) -> Result<u8, FieldError> {
439 let s = self.slice(buf);
440 if s.len() < 7 {
441 return Err(FieldError::BufferTooShort {
442 offset: self.index.start + 6,
443 need: 1,
444 have: s.len().saturating_sub(6),
445 });
446 }
447 Ok(s[6])
448 }
449
450 pub fn set_beam_seqn(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
452 let off = self.index.start + 6;
453 if buf.len() <= off {
454 return Err(FieldError::BufferTooShort {
455 offset: off,
456 need: 1,
457 have: buf.len().saturating_sub(off),
458 });
459 }
460 buf[off] = value;
461 Ok(())
462 }
463
464 pub fn beam_control(&self, buf: &[u8]) -> Result<u8, FieldError> {
466 let bs = self.beam_seqn(buf)?;
467 Ok((bs >> 5) & 0x03)
468 }
469
470 pub fn set_beam_control(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
472 let bs = self.beam_seqn(buf)?;
473 let bs = (bs & !0x60) | ((value & 0x03) << 5);
474 self.set_beam_seqn(buf, bs)
475 }
476
477 pub fn seqn(&self, buf: &[u8]) -> Result<u8, FieldError> {
479 let bs = self.beam_seqn(buf)?;
480 Ok(bs & 0x0F)
481 }
482
483 pub fn set_seqn(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
485 let bs = self.beam_seqn(buf)?;
486 let bs = (bs & 0xF0) | (value & 0x0F);
487 self.set_beam_seqn(buf, bs)
488 }
489
490 pub fn length(&self, buf: &[u8]) -> Result<u8, FieldError> {
492 let s = self.slice(buf);
493 if s.len() < 8 {
494 return Err(FieldError::BufferTooShort {
495 offset: self.index.start + 7,
496 need: 1,
497 have: s.len().saturating_sub(7),
498 });
499 }
500 Ok(s[7])
501 }
502
503 pub fn set_length(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
505 let off = self.index.start + 7;
506 if buf.len() <= off {
507 return Err(FieldError::BufferTooShort {
508 offset: off,
509 need: 1,
510 have: buf.len().saturating_sub(off),
511 });
512 }
513 buf[off] = value;
514 Ok(())
515 }
516
517 pub fn dst(&self, buf: &[u8]) -> Result<u8, FieldError> {
519 let s = self.slice(buf);
520 if s.len() < 9 {
521 return Err(FieldError::BufferTooShort {
522 offset: self.index.start + 8,
523 need: 1,
524 have: s.len().saturating_sub(8),
525 });
526 }
527 Ok(s[8])
528 }
529
530 pub fn set_dst(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
532 let off = self.index.start + 8;
533 if buf.len() <= off {
534 return Err(FieldError::BufferTooShort {
535 offset: off,
536 need: 1,
537 have: buf.len().saturating_sub(off),
538 });
539 }
540 buf[off] = value;
541 Ok(())
542 }
543
544 pub fn crc(&self, buf: &[u8]) -> Result<u8, FieldError> {
547 let s = self.slice(buf);
548 if s.len() < ZWAVE_MIN_HEADER_LEN {
549 return Err(FieldError::BufferTooShort {
550 offset: self.index.start + 9,
551 need: 1,
552 have: s.len().saturating_sub(9),
553 });
554 }
555 Ok(s[s.len() - 1])
557 }
558
559 pub fn set_crc(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
561 let end = self.index.end;
562 if end == 0 || buf.len() < end {
563 return Err(FieldError::BufferTooShort {
564 offset: end.saturating_sub(1),
565 need: 1,
566 have: 0,
567 });
568 }
569 buf[end - 1] = value;
570 Ok(())
571 }
572
573 #[must_use]
579 pub fn is_ack(&self, buf: &[u8]) -> bool {
580 let s = self.slice(buf);
581 s.len() <= ZWAVE_MIN_HEADER_LEN
582 }
583
584 pub fn cmd_class(&self, buf: &[u8]) -> Result<u8, FieldError> {
588 let s = self.slice(buf);
589 if s.len() <= ZWAVE_MIN_HEADER_LEN {
590 return Err(FieldError::InvalidValue(
591 "ACK frame has no cmd_class field".into(),
592 ));
593 }
594 Ok(s[9])
596 }
597
598 pub fn set_cmd_class(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
600 let off = self.index.start + 9;
601 if self.is_ack(buf) {
602 return Err(FieldError::InvalidValue(
603 "ACK frame has no cmd_class field".into(),
604 ));
605 }
606 if buf.len() <= off {
607 return Err(FieldError::BufferTooShort {
608 offset: off,
609 need: 1,
610 have: buf.len().saturating_sub(off),
611 });
612 }
613 buf[off] = value;
614 Ok(())
615 }
616
617 pub fn cmd(&self, buf: &[u8]) -> Result<u8, FieldError> {
619 let s = self.slice(buf);
620 if s.len() <= ZWAVE_MIN_HEADER_LEN + 1 {
621 return Err(FieldError::InvalidValue("frame has no cmd field".into()));
622 }
623 Ok(s[10])
624 }
625
626 pub fn set_cmd(&self, buf: &mut [u8], value: u8) -> Result<(), FieldError> {
628 let off = self.index.start + 10;
629 let s = self.slice(buf);
630 if s.len() <= ZWAVE_MIN_HEADER_LEN + 1 {
631 return Err(FieldError::InvalidValue("frame has no cmd field".into()));
632 }
633 if buf.len() <= off {
634 return Err(FieldError::BufferTooShort {
635 offset: off,
636 need: 1,
637 have: buf.len().saturating_sub(off),
638 });
639 }
640 buf[off] = value;
641 Ok(())
642 }
643
644 pub fn cmd_data<'a>(&self, buf: &'a [u8]) -> Result<&'a [u8], FieldError> {
646 let s = self.slice(buf);
647 if s.len() <= ZWAVE_MIN_HEADER_LEN + 2 {
648 return Ok(&[]);
650 }
651 Ok(&s[11..s.len() - 1])
653 }
654
655 #[must_use]
662 pub fn verify_crc(&self, buf: &[u8]) -> bool {
663 let s = self.slice(buf);
664 if s.len() < ZWAVE_MIN_HEADER_LEN {
665 return false;
666 }
667 let computed = zwave_crc(&s[..s.len() - 1]);
668 computed == s[s.len() - 1]
669 }
670
671 #[must_use]
677 pub fn summary(&self, buf: &[u8]) -> String {
678 let s = self.slice(buf);
679 if s.len() < ZWAVE_MIN_HEADER_LEN {
680 return "Z-Wave".to_string();
681 }
682
683 let home = self.home_id(buf).unwrap_or(0);
684 let src_id = self.src(buf).unwrap_or(0);
685 let dst_id = self.dst(buf).unwrap_or(0);
686
687 if self.is_ack(buf) {
688 format!("Z-Wave ACK {home:#010x} {src_id}->{dst_id}")
689 } else {
690 let cc = self
691 .cmd_class(buf)
692 .map_or_else(|_| "?".to_string(), |c| cmd_class_name(c).to_string());
693 format!("Z-Wave {home:#010x} {src_id}->{dst_id} {cc}")
694 }
695 }
696
697 fn compute_header_len(&self, buf: &[u8]) -> usize {
700 let s = self.slice(buf);
701 s.len()
703 }
704
705 #[must_use]
711 pub fn field_names() -> &'static [&'static str] {
712 ZWAVE_FIELD_NAMES
713 }
714
715 pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
717 match name {
718 "home_id" => Some(self.home_id(buf).map(FieldValue::U32)),
719 "src" => Some(self.src(buf).map(FieldValue::U8)),
720 "dst" => Some(self.dst(buf).map(FieldValue::U8)),
721 "routed" => Some(self.routed(buf).map(FieldValue::Bool)),
722 "ackreq" => Some(self.ackreq(buf).map(FieldValue::Bool)),
723 "lowpower" => Some(self.lowpower(buf).map(FieldValue::Bool)),
724 "speedmodified" => Some(self.speedmodified(buf).map(FieldValue::Bool)),
725 "headertype" => Some(self.headertype(buf).map(FieldValue::U8)),
726 "beam_control" => Some(self.beam_control(buf).map(FieldValue::U8)),
727 "seqn" => Some(self.seqn(buf).map(FieldValue::U8)),
728 "length" => Some(self.length(buf).map(FieldValue::U8)),
729 "cmd_class" => {
730 if self.is_ack(buf) {
731 Some(Ok(FieldValue::U8(0)))
732 } else {
733 Some(self.cmd_class(buf).map(FieldValue::U8))
734 }
735 },
736 "cmd" => {
737 if self.is_ack(buf) {
738 Some(Ok(FieldValue::U8(0)))
739 } else {
740 Some(self.cmd(buf).map(FieldValue::U8))
741 }
742 },
743 "crc" => Some(self.crc(buf).map(FieldValue::U8)),
744 _ => None,
745 }
746 }
747
748 pub fn set_field(
750 &self,
751 buf: &mut [u8],
752 name: &str,
753 value: FieldValue,
754 ) -> Option<Result<(), FieldError>> {
755 match name {
756 "home_id" => {
757 if let FieldValue::U32(v) = value {
758 Some(self.set_home_id(buf, v))
759 } else {
760 Some(Err(FieldError::InvalidValue(format!(
761 "home_id: expected U32, got {value:?}"
762 ))))
763 }
764 },
765 "src" => {
766 if let FieldValue::U8(v) = value {
767 Some(self.set_src(buf, v))
768 } else {
769 Some(Err(FieldError::InvalidValue(format!(
770 "src: expected U8, got {value:?}"
771 ))))
772 }
773 },
774 "dst" => {
775 if let FieldValue::U8(v) = value {
776 Some(self.set_dst(buf, v))
777 } else {
778 Some(Err(FieldError::InvalidValue(format!(
779 "dst: expected U8, got {value:?}"
780 ))))
781 }
782 },
783 "routed" => {
784 if let FieldValue::Bool(v) = value {
785 Some(self.set_routed(buf, v))
786 } else {
787 Some(Err(FieldError::InvalidValue(format!(
788 "routed: expected Bool, got {value:?}"
789 ))))
790 }
791 },
792 "ackreq" => {
793 if let FieldValue::Bool(v) = value {
794 Some(self.set_ackreq(buf, v))
795 } else {
796 Some(Err(FieldError::InvalidValue(format!(
797 "ackreq: expected Bool, got {value:?}"
798 ))))
799 }
800 },
801 "lowpower" => {
802 if let FieldValue::Bool(v) = value {
803 Some(self.set_lowpower(buf, v))
804 } else {
805 Some(Err(FieldError::InvalidValue(format!(
806 "lowpower: expected Bool, got {value:?}"
807 ))))
808 }
809 },
810 "speedmodified" => {
811 if let FieldValue::Bool(v) = value {
812 Some(self.set_speedmodified(buf, v))
813 } else {
814 Some(Err(FieldError::InvalidValue(format!(
815 "speedmodified: expected Bool, got {value:?}"
816 ))))
817 }
818 },
819 "headertype" => {
820 if let FieldValue::U8(v) = value {
821 Some(self.set_headertype(buf, v))
822 } else {
823 Some(Err(FieldError::InvalidValue(format!(
824 "headertype: expected U8, got {value:?}"
825 ))))
826 }
827 },
828 "beam_control" => {
829 if let FieldValue::U8(v) = value {
830 Some(self.set_beam_control(buf, v))
831 } else {
832 Some(Err(FieldError::InvalidValue(format!(
833 "beam_control: expected U8, got {value:?}"
834 ))))
835 }
836 },
837 "seqn" => {
838 if let FieldValue::U8(v) = value {
839 Some(self.set_seqn(buf, v))
840 } else {
841 Some(Err(FieldError::InvalidValue(format!(
842 "seqn: expected U8, got {value:?}"
843 ))))
844 }
845 },
846 "length" => {
847 if let FieldValue::U8(v) = value {
848 Some(self.set_length(buf, v))
849 } else {
850 Some(Err(FieldError::InvalidValue(format!(
851 "length: expected U8, got {value:?}"
852 ))))
853 }
854 },
855 "cmd_class" => {
856 if let FieldValue::U8(v) = value {
857 Some(self.set_cmd_class(buf, v))
858 } else {
859 Some(Err(FieldError::InvalidValue(format!(
860 "cmd_class: expected U8, got {value:?}"
861 ))))
862 }
863 },
864 "cmd" => {
865 if let FieldValue::U8(v) = value {
866 Some(self.set_cmd(buf, v))
867 } else {
868 Some(Err(FieldError::InvalidValue(format!(
869 "cmd: expected U8, got {value:?}"
870 ))))
871 }
872 },
873 "crc" => {
874 if let FieldValue::U8(v) = value {
875 Some(self.set_crc(buf, v))
876 } else {
877 Some(Err(FieldError::InvalidValue(format!(
878 "crc: expected U8, got {value:?}"
879 ))))
880 }
881 },
882 _ => None,
883 }
884 }
885}
886
887impl Layer for ZWaveLayer {
888 fn kind(&self) -> LayerKind {
889 LayerKind::ZWave
890 }
891
892 fn summary(&self, data: &[u8]) -> String {
893 self.summary(data)
894 }
895
896 fn header_len(&self, data: &[u8]) -> usize {
897 self.compute_header_len(data)
898 }
899
900 fn field_names(&self) -> &'static [&'static str] {
901 ZWAVE_FIELD_NAMES
902 }
903
904 fn hashret(&self, data: &[u8]) -> Vec<u8> {
905 self.home_id(data)
907 .map(|h| h.to_be_bytes().to_vec())
908 .unwrap_or_default()
909 }
910
911 fn answers(&self, data: &[u8], other: &Self, other_data: &[u8]) -> bool {
912 let same_home = self.home_id(data) == other.home_id(other_data);
914 let swapped =
915 self.src(data) == other.dst(other_data) && self.dst(data) == other.src(other_data);
916 same_home && swapped
917 }
918}
919
920#[cfg(test)]
921mod tests {
922 use super::*;
923
924 fn ack_frame(home_id: u32, src: u8, dst: u8) -> Vec<u8> {
926 let mut buf = Vec::with_capacity(ZWAVE_MIN_HEADER_LEN);
927 buf.extend_from_slice(&home_id.to_be_bytes()); buf.push(src); buf.push(0x40); buf.push(0x01); buf.push(0x0A); buf.push(dst); let crc = zwave_crc(&buf);
934 buf.push(crc); buf
936 }
937
938 fn req_frame(home_id: u32, src: u8, dst: u8, cmd_class: u8, cmd: u8, data: &[u8]) -> Vec<u8> {
940 let payload_len = 2 + data.len(); let total_len = ZWAVE_MIN_HEADER_LEN + payload_len;
942 let mut buf = Vec::with_capacity(total_len);
943 buf.extend_from_slice(&home_id.to_be_bytes()); buf.push(src); buf.push(0x40); buf.push(0x01); buf.push(total_len as u8); buf.push(dst); buf.push(cmd_class); buf.push(cmd); buf.extend_from_slice(data); let crc = zwave_crc(&buf);
953 buf.push(crc); buf
955 }
956
957 #[test]
958 fn test_crc_computation() {
959 assert_eq!(zwave_crc(&[]), 0xFF);
961 assert_eq!(zwave_crc(&[0xFF]), 0x00);
962 assert_eq!(zwave_crc(&[0x01, 0x02]), 0xFF ^ 0x01 ^ 0x02);
963 assert_eq!(zwave_crc(&[0xAA, 0x55]), 0xFF ^ 0xAA ^ 0x55);
964 }
965
966 #[test]
967 fn test_parse_ack_frame() {
968 let data = ack_frame(0x0161f498, 1, 2);
969 assert_eq!(data.len(), 10);
970
971 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
972 let zw = ZWaveLayer::new(idx);
973
974 assert_eq!(zw.home_id(&data).unwrap(), 0x0161f498);
975 assert_eq!(zw.src(&data).unwrap(), 1);
976 assert_eq!(zw.dst(&data).unwrap(), 2);
977 assert_eq!(zw.length(&data).unwrap(), 10);
978 assert!(zw.is_ack(&data));
979 assert!(zw.verify_crc(&data));
980 }
981
982 #[test]
983 fn test_parse_req_switch_binary() {
984 let data = req_frame(0x0161f498, 1, 2, cmd_class::SWITCH_BINARY, 0x01, &[0xFF]);
985 assert_eq!(data.len(), 13);
987
988 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
989 let zw = ZWaveLayer::new(idx);
990
991 assert!(!zw.is_ack(&data));
992 assert_eq!(zw.cmd_class(&data).unwrap(), cmd_class::SWITCH_BINARY);
993 assert_eq!(zw.cmd(&data).unwrap(), 0x01);
994 assert_eq!(zw.cmd_data(&data).unwrap(), &[0xFF]);
995 assert!(zw.verify_crc(&data));
996 }
997
998 #[test]
999 fn test_parse_req_sensor_binary() {
1000 let data = req_frame(0xDEADBEEF, 3, 5, cmd_class::SENSOR_BINARY, 0x03, &[0xFF]);
1001 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1002 let zw = ZWaveLayer::new(idx);
1003
1004 assert!(!zw.is_ack(&data));
1005 assert_eq!(zw.home_id(&data).unwrap(), 0xDEADBEEF);
1006 assert_eq!(zw.cmd_class(&data).unwrap(), cmd_class::SENSOR_BINARY);
1007 assert_eq!(cmd_class_name(cmd_class::SENSOR_BINARY), "SENSOR_BINARY");
1008 assert!(zw.verify_crc(&data));
1009 }
1010
1011 #[test]
1012 fn test_frame_ctrl_bitfields() {
1013 let mut data = ack_frame(0x0161f498, 1, 2);
1014 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1015 let zw = ZWaveLayer::new(idx);
1016
1017 assert!(zw.ackreq(&data).unwrap());
1019 assert!(!zw.routed(&data).unwrap());
1020 assert!(!zw.lowpower(&data).unwrap());
1021 assert!(!zw.speedmodified(&data).unwrap());
1022 assert_eq!(zw.headertype(&data).unwrap(), 0);
1023
1024 zw.set_routed(&mut data, true).unwrap();
1026 assert!(zw.routed(&data).unwrap());
1027 assert!(zw.ackreq(&data).unwrap()); zw.set_headertype(&mut data, 0x05).unwrap();
1031 assert_eq!(zw.headertype(&data).unwrap(), 0x05);
1032 assert!(zw.routed(&data).unwrap()); }
1034
1035 #[test]
1036 fn test_beam_seqn_bitfields() {
1037 let mut data = ack_frame(0x0161f498, 1, 2);
1038 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1039 let zw = ZWaveLayer::new(idx);
1040
1041 assert_eq!(zw.beam_control(&data).unwrap(), 0);
1043 assert_eq!(zw.seqn(&data).unwrap(), 1);
1044
1045 zw.set_beam_control(&mut data, 2).unwrap();
1047 assert_eq!(zw.beam_control(&data).unwrap(), 2);
1048 assert_eq!(zw.seqn(&data).unwrap(), 1); zw.set_seqn(&mut data, 0x0F).unwrap();
1052 assert_eq!(zw.seqn(&data).unwrap(), 0x0F);
1053 assert_eq!(zw.beam_control(&data).unwrap(), 2); }
1055
1056 #[test]
1057 fn test_cmd_class_name_helper() {
1058 assert_eq!(cmd_class_name(cmd_class::NO_OPERATION), "NO_OPERATION");
1059 assert_eq!(cmd_class_name(cmd_class::SWITCH_BINARY), "SWITCH_BINARY");
1060 assert_eq!(cmd_class_name(cmd_class::SENSOR_BINARY), "SENSOR_BINARY");
1061 assert_eq!(cmd_class_name(cmd_class::BATTERY), "BATTERY");
1062 assert_eq!(cmd_class_name(cmd_class::SECURITY), "SECURITY");
1063 assert_eq!(
1064 cmd_class_name(cmd_class::NON_INTEROPERABLE),
1065 "NON_INTEROPERABLE"
1066 );
1067 assert_eq!(cmd_class_name(0x99), "AV_TAGGING_MD");
1068 assert_eq!(cmd_class_name(0xBB), "UNKNOWN");
1069 }
1070
1071 #[test]
1072 fn test_is_ack_detection() {
1073 let ack = ack_frame(0x0161f498, 1, 2);
1074 let idx = LayerIndex::new(LayerKind::ZWave, 0, ack.len());
1075 let zw = ZWaveLayer::new(idx);
1076 assert!(zw.is_ack(&ack));
1077
1078 let req = req_frame(0x0161f498, 1, 2, cmd_class::BASIC, 0x01, &[]);
1079 let idx = LayerIndex::new(LayerKind::ZWave, 0, req.len());
1080 let zw = ZWaveLayer::new(idx);
1081 assert!(!zw.is_ack(&req));
1082 }
1083
1084 #[test]
1085 fn test_verify_crc_valid() {
1086 let data = req_frame(0x0161f498, 1, 2, cmd_class::SWITCH_BINARY, 0x01, &[0xFF]);
1087 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1088 let zw = ZWaveLayer::new(idx);
1089 assert!(zw.verify_crc(&data));
1090 }
1091
1092 #[test]
1093 fn test_verify_crc_invalid() {
1094 let mut data = req_frame(0x0161f498, 1, 2, cmd_class::SWITCH_BINARY, 0x01, &[0xFF]);
1095 let last = data.len() - 1;
1097 data[last] ^= 0x01;
1098 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1099 let zw = ZWaveLayer::new(idx);
1100 assert!(!zw.verify_crc(&data));
1101 }
1102
1103 #[test]
1104 fn test_summary_ack() {
1105 let data = ack_frame(0x0161f498, 1, 2);
1106 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1107 let zw = ZWaveLayer::new(idx);
1108 let s = zw.summary(&data);
1109 assert!(s.contains("ACK"));
1110 assert!(s.contains("0x0161f498"));
1111 assert!(s.contains("1->2"));
1112 }
1113
1114 #[test]
1115 fn test_summary_req() {
1116 let data = req_frame(0x0161f498, 1, 2, cmd_class::SWITCH_BINARY, 0x01, &[0xFF]);
1117 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1118 let zw = ZWaveLayer::new(idx);
1119 let s = zw.summary(&data);
1120 assert!(s.contains("SWITCH_BINARY"));
1121 assert!(s.contains("0x0161f498"));
1122 assert!(s.contains("1->2"));
1123 assert!(!s.contains("ACK"));
1124 }
1125
1126 #[test]
1127 fn test_get_field() {
1128 let data = req_frame(0x0161f498, 1, 2, cmd_class::SWITCH_BINARY, 0x01, &[0xFF]);
1129 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1130 let zw = ZWaveLayer::new(idx);
1131
1132 assert_eq!(
1133 zw.get_field(&data, "home_id").unwrap().unwrap(),
1134 FieldValue::U32(0x0161f498)
1135 );
1136 assert_eq!(
1137 zw.get_field(&data, "src").unwrap().unwrap(),
1138 FieldValue::U8(1)
1139 );
1140 assert_eq!(
1141 zw.get_field(&data, "dst").unwrap().unwrap(),
1142 FieldValue::U8(2)
1143 );
1144 assert_eq!(
1145 zw.get_field(&data, "ackreq").unwrap().unwrap(),
1146 FieldValue::Bool(true)
1147 );
1148 assert_eq!(
1149 zw.get_field(&data, "cmd_class").unwrap().unwrap(),
1150 FieldValue::U8(cmd_class::SWITCH_BINARY)
1151 );
1152 assert!(zw.get_field(&data, "nonexistent").is_none());
1153 }
1154
1155 #[test]
1156 fn test_builder_round_trip() {
1157 let built = ZWaveBuilder::new()
1158 .home_id(0x0161f498)
1159 .src(1)
1160 .dst(2)
1161 .cmd_class(cmd_class::SWITCH_BINARY)
1162 .cmd(0x01)
1163 .cmd_data(vec![0xFF])
1164 .build();
1165
1166 let idx = LayerIndex::new(LayerKind::ZWave, 0, built.len());
1167 let zw = ZWaveLayer::new(idx);
1168
1169 assert_eq!(zw.home_id(&built).unwrap(), 0x0161f498);
1170 assert_eq!(zw.src(&built).unwrap(), 1);
1171 assert_eq!(zw.dst(&built).unwrap(), 2);
1172 assert_eq!(zw.cmd_class(&built).unwrap(), cmd_class::SWITCH_BINARY);
1173 assert_eq!(zw.cmd(&built).unwrap(), 0x01);
1174 assert_eq!(zw.cmd_data(&built).unwrap(), &[0xFF]);
1175 assert!(zw.verify_crc(&built));
1176 }
1177
1178 #[test]
1179 fn test_is_zwave_frame_detection() {
1180 let ack = ack_frame(0x0161f498, 1, 2);
1182 assert!(is_zwave_frame(&ack));
1183
1184 let req = req_frame(0x0161f498, 1, 2, cmd_class::BASIC, 0x01, &[]);
1186 assert!(is_zwave_frame(&req));
1187
1188 assert!(!is_zwave_frame(&[0x00; 9]));
1190
1191 let mut bad = ack.clone();
1193 bad[7] = 0xFF; assert!(!is_zwave_frame(&bad));
1195
1196 let mut bad2 = ack.clone();
1198 bad2[7] = 5; assert!(!is_zwave_frame(&bad2));
1200 }
1201
1202 #[test]
1203 fn test_set_field_home_id() {
1204 let mut data = ack_frame(0x0161f498, 1, 2);
1205 let idx = LayerIndex::new(LayerKind::ZWave, 0, data.len());
1206 let zw = ZWaveLayer::new(idx);
1207
1208 zw.set_field(&mut data, "home_id", FieldValue::U32(0xAABBCCDD))
1209 .unwrap()
1210 .unwrap();
1211 assert_eq!(zw.home_id(&data).unwrap(), 0xAABBCCDD);
1212 }
1213
1214 #[test]
1215 fn test_hashret_and_answers() {
1216 let frame1 = req_frame(0x0161f498, 1, 2, cmd_class::BASIC, 0x01, &[]);
1217 let frame2 = req_frame(0x0161f498, 2, 1, cmd_class::BASIC, 0x03, &[]);
1218
1219 let idx1 = LayerIndex::new(LayerKind::ZWave, 0, frame1.len());
1220 let zw1 = ZWaveLayer::new(idx1);
1221
1222 let idx2 = LayerIndex::new(LayerKind::ZWave, 0, frame2.len());
1223 let zw2 = ZWaveLayer::new(idx2);
1224
1225 assert_eq!(zw1.hashret(&frame1), zw2.hashret(&frame2));
1227
1228 assert!(zw1.answers(&frame1, &zw2, &frame2));
1230 assert!(zw2.answers(&frame2, &zw1, &frame1));
1231
1232 let frame3 = req_frame(0xDEADBEEF, 2, 1, cmd_class::BASIC, 0x03, &[]);
1234 let idx3 = LayerIndex::new(LayerKind::ZWave, 0, frame3.len());
1235 let zw3 = ZWaveLayer::new(idx3);
1236 assert!(!zw1.answers(&frame1, &zw3, &frame3));
1237 }
1238}