1use anyhow::{anyhow, Result};
2use rusb::{Device, DeviceHandle, GlobalContext};
3use std::sync::{Arc, Mutex};
4use std::time::Duration;
5use thiserror::Error;
6use tokio::time::sleep;
7
8const VENDOR_ID: u16 = 0x191A;
9const DEVICE_ID: u16 = 0x6001;
10const COMMAND_VERSION: u8 = 0x0;
11const COMMAND_ID_CONTROL: u8 = 0x0;
12const COMMAND_ID_SETTING: u8 = 0x1;
13const COMMAND_ID_GETSTATE: u8 = 0x80;
14const ENDPOINT_ADDRESS: u8 = 0x01;
15const ENDPOINT_ADDRESS_GET: u8 = 0x81;
16const SEND_TIMEOUT: Duration = Duration::from_millis(1000);
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[repr(u8)]
20pub enum LedColor {
21 Off = 0,
22 Red = 1,
23 Green = 2,
24 Yellow = 3,
25 Blue = 4,
26 Purple = 5,
27 LightBlue = 6,
28 White = 7,
29 Keep = 0xF,
30}
31
32impl LedColor {
33 pub fn from_str(s: &str) -> Option<Self> {
34 match s.to_lowercase().as_str() {
35 "off" => Some(LedColor::Off),
36 "red" => Some(LedColor::Red),
37 "green" => Some(LedColor::Green),
38 "yellow" => Some(LedColor::Yellow),
39 "blue" => Some(LedColor::Blue),
40 "purple" => Some(LedColor::Purple),
41 "lightblue" | "light-blue" | "skyblue" | "sky-blue" => Some(LedColor::LightBlue),
42 "white" => Some(LedColor::White),
43 _ => None,
44 }
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49#[repr(u8)]
50pub enum LedPattern {
51 Off = 0x0,
52 On = 0x1,
53 Pattern1 = 0x2,
54 Pattern2 = 0x3,
55 Pattern3 = 0x4,
56 Pattern4 = 0x5,
57 Pattern5 = 0x6,
58 Pattern6 = 0x7,
59 Keep = 0xF,
60}
61
62impl LedPattern {
63 pub fn from_str(s: &str) -> Option<Self> {
64 match s.to_lowercase().as_str() {
65 "off" => Some(LedPattern::Off),
66 "on" | "solid" => Some(LedPattern::On),
67 "pattern1" | "1" => Some(LedPattern::Pattern1),
68 "pattern2" | "2" => Some(LedPattern::Pattern2),
69 "pattern3" | "3" => Some(LedPattern::Pattern3),
70 "pattern4" | "4" => Some(LedPattern::Pattern4),
71 "pattern5" | "5" => Some(LedPattern::Pattern5),
72 "pattern6" | "6" => Some(LedPattern::Pattern6),
73 _ => None,
74 }
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[repr(u8)]
80pub enum BuzzerPattern {
81 Off = 0x0,
82 On = 0x1,
83 Sweep = 0x2,
84 Intermittent = 0x3,
85 WeakAttention = 0x4,
86 StrongAttention = 0x5,
87 ShiningStar = 0x6,
88 LondonBridge = 0x7,
89 Keep = 0xF,
90}
91
92impl BuzzerPattern {
93 pub fn from_str(s: &str) -> Option<Self> {
94 match s.to_lowercase().as_str() {
95 "off" => Some(BuzzerPattern::Off),
96 "on" | "continuous" => Some(BuzzerPattern::On),
97 "sweep" => Some(BuzzerPattern::Sweep),
98 "intermittent" => Some(BuzzerPattern::Intermittent),
99 "weak" | "weak-attention" => Some(BuzzerPattern::WeakAttention),
100 "strong" | "strong-attention" => Some(BuzzerPattern::StrongAttention),
101 "shining-star" => Some(BuzzerPattern::ShiningStar),
102 "london-bridge" => Some(BuzzerPattern::LondonBridge),
103 _ => None,
104 }
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub struct BuzzerCount(u8);
110
111impl BuzzerCount {
112 pub const CONTINUOUS: Self = Self(0x0);
113 pub const KEEP: Self = Self(0xF);
114
115 pub fn times(count: u8) -> Option<Self> {
116 if count >= 1 && count <= 14 {
117 Some(Self(count))
118 } else {
119 None
120 }
121 }
122
123 pub fn value(&self) -> u8 {
124 self.0
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub struct BuzzerVolume(u8);
130
131impl BuzzerVolume {
132 pub const OFF: Self = Self(0x0);
133 pub const MAX: Self = Self(0xA);
134 pub const KEEP: Self = Self(0xF);
135
136 pub fn level(level: u8) -> Option<Self> {
137 if level <= 0xA {
138 Some(Self(level))
139 } else {
140 None
141 }
142 }
143
144 pub fn value(&self) -> u8 {
145 self.0
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150#[repr(u8)]
151pub enum Setting {
152 Off = 0x0,
153 On = 0x1,
154}
155
156#[derive(Debug, Error)]
157pub enum BeaconError {
158 #[error("USB error: {0}")]
159 Usb(#[from] rusb::Error),
160
161 #[error("Device not found")]
162 DeviceNotFound,
163
164 #[error("Failed to send command")]
165 SendFailed,
166
167 #[error("Invalid parameter")]
168 InvalidParameter,
169}
170
171pub struct Beacon {
172 handle: DeviceHandle<GlobalContext>,
173 _device: Device<GlobalContext>,
174}
175
176impl Beacon {
177 pub fn open() -> Result<Self> {
178 let devices = rusb::devices()?;
179
180 for device in devices.iter() {
181 let device_desc = device.device_descriptor()?;
182
183 if device_desc.vendor_id() == VENDOR_ID && device_desc.product_id() == DEVICE_ID {
184 let handle = device.open()?;
185
186 if let Ok(active) = handle.kernel_driver_active(0) {
188 if active {
189 handle.detach_kernel_driver(0)?;
190 }
191 }
192
193 handle.set_active_configuration(1)?;
195
196 handle.claim_interface(0)?;
198
199 return Ok(Beacon {
200 handle,
201 _device: device,
202 });
203 }
204 }
205
206 Err(anyhow!("Device not found"))
207 }
208
209 pub fn scan() -> Result<Vec<(u8, u8, u16, u16)>> {
210 let devices = rusb::devices()?;
211 let mut found = Vec::new();
212
213 for device in devices.iter() {
214 let device_desc = device.device_descriptor()?;
215
216 if device_desc.vendor_id() == VENDOR_ID && device_desc.product_id() == DEVICE_ID {
217 found.push((
218 device.bus_number(),
219 device.address(),
220 device_desc.vendor_id(),
221 device_desc.product_id(),
222 ));
223 }
224 }
225
226 Ok(found)
227 }
228
229 fn send_command(&self, data: &[u8]) -> Result<()> {
230 let bytes_written = self.handle.write_bulk(ENDPOINT_ADDRESS, data, SEND_TIMEOUT)?;
231
232 if bytes_written != data.len() {
233 return Err(anyhow!("Failed to send complete command"));
234 }
235
236 Ok(())
237 }
238
239 pub fn set_light(&self, color: LedColor, pattern: LedPattern) -> Result<()> {
240 let buzzer_control = (BuzzerCount::KEEP.value() << 4) | BuzzerPattern::Keep as u8;
241 let led_control = ((color as u8) << 4) | (pattern as u8);
242
243 let data = [
244 COMMAND_VERSION,
245 COMMAND_ID_CONTROL,
246 buzzer_control,
247 BuzzerVolume::KEEP.value(),
248 led_control,
249 0, 0, 0,
250 ];
251
252 self.send_command(&data)
253 }
254
255 pub fn set_buzzer(&self, pattern: BuzzerPattern, count: BuzzerCount) -> Result<()> {
256 let buzzer_control = (count.value() << 4) | (pattern as u8);
257 let led_control = ((LedColor::Keep as u8) << 4) | (LedPattern::Keep as u8);
258
259 let data = [
260 COMMAND_VERSION,
261 COMMAND_ID_CONTROL,
262 buzzer_control,
263 BuzzerVolume::KEEP.value(),
264 led_control,
265 0, 0, 0,
266 ];
267
268 self.send_command(&data)
269 }
270
271 pub fn set_volume(&self, volume: BuzzerVolume) -> Result<()> {
272 let buzzer_control = (BuzzerCount::KEEP.value() << 4) | (BuzzerPattern::Keep as u8);
273 let led_control = ((LedColor::Keep as u8) << 4) | (LedPattern::Keep as u8);
274
275 let data = [
276 COMMAND_VERSION,
277 COMMAND_ID_CONTROL,
278 buzzer_control,
279 volume.value(),
280 led_control,
281 0, 0, 0,
282 ];
283
284 self.send_command(&data)
285 }
286
287 pub fn set_buzzer_ex(&self, pattern: BuzzerPattern, count: BuzzerCount, volume: BuzzerVolume) -> Result<()> {
288 let buzzer_control = (count.value() << 4) | (pattern as u8);
289 let led_control = ((LedColor::Keep as u8) << 4) | (LedPattern::Keep as u8);
290
291 let data = [
292 COMMAND_VERSION,
293 COMMAND_ID_CONTROL,
294 buzzer_control,
295 volume.value(),
296 led_control,
297 0, 0, 0,
298 ];
299
300 self.send_command(&data)
301 }
302
303 pub fn set_setting(&self, setting: Setting) -> Result<()> {
304 let data = [
305 COMMAND_VERSION,
306 COMMAND_ID_SETTING,
307 setting as u8,
308 0, 0, 0, 0, 0,
309 ];
310
311 self.send_command(&data)
312 }
313
314 pub fn get_touch_sensor_state(&self) -> Result<bool> {
315 let data = [
316 COMMAND_VERSION,
317 COMMAND_ID_GETSTATE,
318 0, 0, 0, 0, 0, 0,
319 ];
320
321 self.send_command(&data)?;
322
323 let mut response = [0u8; 2];
324 self.handle.read_bulk(ENDPOINT_ADDRESS_GET, &mut response, SEND_TIMEOUT)?;
325
326 Ok((response[1] & 1) == 1)
327 }
328
329 pub fn reset(&self) -> Result<()> {
330 let buzzer_control = (BuzzerCount::KEEP.value() << 4) | (BuzzerPattern::Off as u8);
331 let led_control = ((LedColor::Off as u8) << 4) | (LedPattern::Off as u8);
332
333 let data = [
334 COMMAND_VERSION,
335 COMMAND_ID_CONTROL,
336 buzzer_control,
337 BuzzerVolume::KEEP.value(),
338 led_control,
339 0, 0, 0,
340 ];
341
342 self.send_command(&data)
343 }
344
345 pub fn wait_for_touch_sync(&self) -> Result<()> {
346 let initial_state = self.get_touch_sensor_state()?;
347
348 if initial_state {
349 while self.get_touch_sensor_state()? {
350 std::thread::sleep(Duration::from_millis(50));
351 }
352 }
353
354 while !self.get_touch_sensor_state()? {
355 std::thread::sleep(Duration::from_millis(50));
356 }
357
358 Ok(())
359 }
360
361 pub fn wait_for_touch_with_callback_sync<F>(&self, mut callback: F) -> Result<()>
362 where
363 F: FnMut(bool),
364 {
365 let initial_state = self.get_touch_sensor_state()?;
366 let mut last_state = initial_state;
367 callback(last_state);
368
369 if initial_state {
370 while self.get_touch_sensor_state()? {
371 std::thread::sleep(Duration::from_millis(50));
372 }
373 last_state = false;
374 callback(last_state);
375 }
376
377 while !self.get_touch_sensor_state()? {
378 std::thread::sleep(Duration::from_millis(50));
379 }
380 callback(true);
381
382 Ok(())
383 }
384
385 pub fn poll_touch_sensor_sync<F>(&self, mut callback: F, poll_interval: Duration) -> Result<()>
386 where
387 F: FnMut(bool) -> bool,
388 {
389 loop {
390 let state = self.get_touch_sensor_state()?;
391 if !callback(state) {
392 break;
393 }
394 std::thread::sleep(poll_interval);
395 }
396 Ok(())
397 }
398
399}
400
401pub struct AsyncBeacon {
402 beacon: Arc<Mutex<Beacon>>,
403}
404
405impl AsyncBeacon {
406 pub fn new(beacon: Beacon) -> Self {
407 Self {
408 beacon: Arc::new(Mutex::new(beacon)),
409 }
410 }
411
412 pub async fn wait_for_touch(&self) -> Result<()> {
413 let initial_state = {
414 let beacon = self.beacon.lock().unwrap();
415 beacon.get_touch_sensor_state()?
416 };
417
418 if initial_state {
419 loop {
420 let state = {
421 let beacon = self.beacon.lock().unwrap();
422 beacon.get_touch_sensor_state()?
423 };
424 if !state {
425 break;
426 }
427 sleep(Duration::from_millis(50)).await;
428 }
429 }
430
431 loop {
432 let state = {
433 let beacon = self.beacon.lock().unwrap();
434 beacon.get_touch_sensor_state()?
435 };
436 if state {
437 break;
438 }
439 sleep(Duration::from_millis(50)).await;
440 }
441
442 Ok(())
443 }
444
445 pub async fn wait_for_touch_with_callback<F>(&self, mut callback: F) -> Result<()>
446 where
447 F: FnMut(bool),
448 {
449 let initial_state = {
450 let beacon = self.beacon.lock().unwrap();
451 beacon.get_touch_sensor_state()?
452 };
453 let mut last_state = initial_state;
454 callback(last_state);
455
456 if initial_state {
457 loop {
458 let state = {
459 let beacon = self.beacon.lock().unwrap();
460 beacon.get_touch_sensor_state()?
461 };
462 if !state {
463 last_state = false;
464 callback(last_state);
465 break;
466 }
467 sleep(Duration::from_millis(50)).await;
468 }
469 }
470
471 loop {
472 let state = {
473 let beacon = self.beacon.lock().unwrap();
474 beacon.get_touch_sensor_state()?
475 };
476 if state {
477 callback(true);
478 break;
479 }
480 sleep(Duration::from_millis(50)).await;
481 }
482
483 Ok(())
484 }
485
486 pub async fn poll_touch_sensor<F>(&self, mut callback: F, poll_interval: Duration) -> Result<()>
487 where
488 F: FnMut(bool) -> bool,
489 {
490 loop {
491 let state = {
492 let beacon = self.beacon.lock().unwrap();
493 beacon.get_touch_sensor_state()?
494 };
495 if !callback(state) {
496 break;
497 }
498 sleep(poll_interval).await;
499 }
500 Ok(())
501 }
502}
503
504impl Drop for Beacon {
505 fn drop(&mut self) {
506 let _ = self.handle.release_interface(0);
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514
515 #[test]
516 fn test_led_color_from_str() {
517 assert_eq!(LedColor::from_str("off"), Some(LedColor::Off));
518 assert_eq!(LedColor::from_str("red"), Some(LedColor::Red));
519 assert_eq!(LedColor::from_str("RED"), Some(LedColor::Red));
520 assert_eq!(LedColor::from_str("green"), Some(LedColor::Green));
521 assert_eq!(LedColor::from_str("yellow"), Some(LedColor::Yellow));
522 assert_eq!(LedColor::from_str("blue"), Some(LedColor::Blue));
523 assert_eq!(LedColor::from_str("purple"), Some(LedColor::Purple));
524 assert_eq!(LedColor::from_str("lightblue"), Some(LedColor::LightBlue));
525 assert_eq!(LedColor::from_str("light-blue"), Some(LedColor::LightBlue));
526 assert_eq!(LedColor::from_str("skyblue"), Some(LedColor::LightBlue));
527 assert_eq!(LedColor::from_str("sky-blue"), Some(LedColor::LightBlue));
528 assert_eq!(LedColor::from_str("white"), Some(LedColor::White));
529 assert_eq!(LedColor::from_str("invalid"), None);
530 }
531
532 #[test]
533 fn test_led_color_values() {
534 assert_eq!(LedColor::Off as u8, 0);
535 assert_eq!(LedColor::Red as u8, 1);
536 assert_eq!(LedColor::Green as u8, 2);
537 assert_eq!(LedColor::Yellow as u8, 3);
538 assert_eq!(LedColor::Blue as u8, 4);
539 assert_eq!(LedColor::Purple as u8, 5);
540 assert_eq!(LedColor::LightBlue as u8, 6);
541 assert_eq!(LedColor::White as u8, 7);
542 assert_eq!(LedColor::Keep as u8, 0xF);
543 }
544
545 #[test]
546 fn test_led_pattern_from_str() {
547 assert_eq!(LedPattern::from_str("off"), Some(LedPattern::Off));
548 assert_eq!(LedPattern::from_str("on"), Some(LedPattern::On));
549 assert_eq!(LedPattern::from_str("solid"), Some(LedPattern::On));
550 assert_eq!(LedPattern::from_str("pattern1"), Some(LedPattern::Pattern1));
551 assert_eq!(LedPattern::from_str("1"), Some(LedPattern::Pattern1));
552 assert_eq!(LedPattern::from_str("pattern2"), Some(LedPattern::Pattern2));
553 assert_eq!(LedPattern::from_str("2"), Some(LedPattern::Pattern2));
554 assert_eq!(LedPattern::from_str("pattern3"), Some(LedPattern::Pattern3));
555 assert_eq!(LedPattern::from_str("3"), Some(LedPattern::Pattern3));
556 assert_eq!(LedPattern::from_str("pattern4"), Some(LedPattern::Pattern4));
557 assert_eq!(LedPattern::from_str("4"), Some(LedPattern::Pattern4));
558 assert_eq!(LedPattern::from_str("pattern5"), Some(LedPattern::Pattern5));
559 assert_eq!(LedPattern::from_str("5"), Some(LedPattern::Pattern5));
560 assert_eq!(LedPattern::from_str("pattern6"), Some(LedPattern::Pattern6));
561 assert_eq!(LedPattern::from_str("6"), Some(LedPattern::Pattern6));
562 assert_eq!(LedPattern::from_str("PATTERN1"), Some(LedPattern::Pattern1));
563 assert_eq!(LedPattern::from_str("invalid"), None);
564 }
565
566 #[test]
567 fn test_led_pattern_values() {
568 assert_eq!(LedPattern::Off as u8, 0x0);
569 assert_eq!(LedPattern::On as u8, 0x1);
570 assert_eq!(LedPattern::Pattern1 as u8, 0x2);
571 assert_eq!(LedPattern::Pattern2 as u8, 0x3);
572 assert_eq!(LedPattern::Pattern3 as u8, 0x4);
573 assert_eq!(LedPattern::Pattern4 as u8, 0x5);
574 assert_eq!(LedPattern::Pattern5 as u8, 0x6);
575 assert_eq!(LedPattern::Pattern6 as u8, 0x7);
576 assert_eq!(LedPattern::Keep as u8, 0xF);
577 }
578
579 #[test]
580 fn test_buzzer_pattern_from_str() {
581 assert_eq!(BuzzerPattern::from_str("off"), Some(BuzzerPattern::Off));
582 assert_eq!(BuzzerPattern::from_str("on"), Some(BuzzerPattern::On));
583 assert_eq!(BuzzerPattern::from_str("continuous"), Some(BuzzerPattern::On));
584 assert_eq!(BuzzerPattern::from_str("sweep"), Some(BuzzerPattern::Sweep));
585 assert_eq!(BuzzerPattern::from_str("intermittent"), Some(BuzzerPattern::Intermittent));
586 assert_eq!(BuzzerPattern::from_str("weak"), Some(BuzzerPattern::WeakAttention));
587 assert_eq!(BuzzerPattern::from_str("weak-attention"), Some(BuzzerPattern::WeakAttention));
588 assert_eq!(BuzzerPattern::from_str("strong"), Some(BuzzerPattern::StrongAttention));
589 assert_eq!(BuzzerPattern::from_str("strong-attention"), Some(BuzzerPattern::StrongAttention));
590 assert_eq!(BuzzerPattern::from_str("shining-star"), Some(BuzzerPattern::ShiningStar));
591 assert_eq!(BuzzerPattern::from_str("london-bridge"), Some(BuzzerPattern::LondonBridge));
592 assert_eq!(BuzzerPattern::from_str("SWEEP"), Some(BuzzerPattern::Sweep));
593 assert_eq!(BuzzerPattern::from_str("invalid"), None);
594 }
595
596 #[test]
597 fn test_buzzer_pattern_values() {
598 assert_eq!(BuzzerPattern::Off as u8, 0x0);
599 assert_eq!(BuzzerPattern::On as u8, 0x1);
600 assert_eq!(BuzzerPattern::Sweep as u8, 0x2);
601 assert_eq!(BuzzerPattern::Intermittent as u8, 0x3);
602 assert_eq!(BuzzerPattern::WeakAttention as u8, 0x4);
603 assert_eq!(BuzzerPattern::StrongAttention as u8, 0x5);
604 assert_eq!(BuzzerPattern::ShiningStar as u8, 0x6);
605 assert_eq!(BuzzerPattern::LondonBridge as u8, 0x7);
606 assert_eq!(BuzzerPattern::Keep as u8, 0xF);
607 }
608
609 #[test]
610 fn test_buzzer_count_times() {
611 assert_eq!(BuzzerCount::times(0), None);
612 assert_eq!(BuzzerCount::times(1), Some(BuzzerCount(1)));
613 assert_eq!(BuzzerCount::times(7), Some(BuzzerCount(7)));
614 assert_eq!(BuzzerCount::times(14), Some(BuzzerCount(14)));
615 assert_eq!(BuzzerCount::times(15), None);
616 assert_eq!(BuzzerCount::times(100), None);
617 }
618
619 #[test]
620 fn test_buzzer_count_constants() {
621 assert_eq!(BuzzerCount::CONTINUOUS.value(), 0x0);
622 assert_eq!(BuzzerCount::KEEP.value(), 0xF);
623 }
624
625 #[test]
626 fn test_buzzer_count_value() {
627 let count = BuzzerCount::times(5).unwrap();
628 assert_eq!(count.value(), 5);
629 }
630
631 #[test]
632 fn test_buzzer_volume_level() {
633 assert_eq!(BuzzerVolume::level(0), Some(BuzzerVolume(0)));
634 assert_eq!(BuzzerVolume::level(5), Some(BuzzerVolume(5)));
635 assert_eq!(BuzzerVolume::level(10), Some(BuzzerVolume(10)));
636 assert_eq!(BuzzerVolume::level(11), None);
637 assert_eq!(BuzzerVolume::level(100), None);
638 }
639
640 #[test]
641 fn test_buzzer_volume_constants() {
642 assert_eq!(BuzzerVolume::OFF.value(), 0x0);
643 assert_eq!(BuzzerVolume::MAX.value(), 0xA);
644 assert_eq!(BuzzerVolume::KEEP.value(), 0xF);
645 }
646
647 #[test]
648 fn test_buzzer_volume_value() {
649 let volume = BuzzerVolume::level(7).unwrap();
650 assert_eq!(volume.value(), 7);
651 }
652
653 #[test]
654 fn test_setting_values() {
655 assert_eq!(Setting::Off as u8, 0x0);
656 assert_eq!(Setting::On as u8, 0x1);
657 }
658
659 #[test]
660 fn test_command_byte_generation_led() {
661 let color = LedColor::Red as u8;
662 let pattern = LedPattern::Pattern1 as u8;
663 let led_control = (color << 4) | pattern;
664 assert_eq!(led_control, 0x12);
665
666 let color = LedColor::Green as u8;
667 let pattern = LedPattern::On as u8;
668 let led_control = (color << 4) | pattern;
669 assert_eq!(led_control, 0x21);
670
671 let color = LedColor::Keep as u8;
672 let pattern = LedPattern::Keep as u8;
673 let led_control = (color << 4) | pattern;
674 assert_eq!(led_control, 0xFF);
675 }
676
677 #[test]
678 fn test_command_byte_generation_buzzer() {
679 let count = BuzzerCount::times(3).unwrap().value();
680 let pattern = BuzzerPattern::Sweep as u8;
681 let buzzer_control = (count << 4) | pattern;
682 assert_eq!(buzzer_control, 0x32);
683
684 let count = BuzzerCount::CONTINUOUS.value();
685 let pattern = BuzzerPattern::On as u8;
686 let buzzer_control = (count << 4) | pattern;
687 assert_eq!(buzzer_control, 0x01);
688
689 let count = BuzzerCount::KEEP.value();
690 let pattern = BuzzerPattern::Keep as u8;
691 let buzzer_control = (count << 4) | pattern;
692 assert_eq!(buzzer_control, 0xFF);
693 }
694
695 #[test]
696 fn test_full_command_construction() {
697 let buzzer_count = BuzzerCount::times(2).unwrap().value();
698 let buzzer_pattern = BuzzerPattern::Intermittent as u8;
699 let buzzer_control = (buzzer_count << 4) | buzzer_pattern;
700
701 let led_color = LedColor::Blue as u8;
702 let led_pattern = LedPattern::Pattern3 as u8;
703 let led_control = (led_color << 4) | led_pattern;
704
705 let volume = BuzzerVolume::level(5).unwrap().value();
706
707 let data = [
708 COMMAND_VERSION,
709 COMMAND_ID_CONTROL,
710 buzzer_control,
711 volume,
712 led_control,
713 0, 0, 0,
714 ];
715
716 assert_eq!(data[0], 0x0);
717 assert_eq!(data[1], 0x0);
718 assert_eq!(data[2], 0x23);
719 assert_eq!(data[3], 0x5);
720 assert_eq!(data[4], 0x44);
721 }
722
723 #[test]
724 fn test_touch_sensor_command_construction() {
725 let data = [
726 COMMAND_VERSION,
727 COMMAND_ID_GETSTATE,
728 0, 0, 0, 0, 0, 0,
729 ];
730
731 assert_eq!(data[0], 0x0);
732 assert_eq!(data[1], 0x80);
733 assert_eq!(data.len(), 8);
734 }
735
736 #[test]
737 fn test_async_beacon_creation() {
738 fn _type_check() {
744 fn create_async_beacon(beacon: Beacon) -> AsyncBeacon {
746 AsyncBeacon::new(beacon)
747 }
748 }
749 }
750
751 #[test]
752 fn test_poll_interval_duration() {
753 use std::time::Duration;
754
755 let poll_interval = Duration::from_millis(50);
757 assert_eq!(poll_interval.as_millis(), 50);
758
759 let poll_interval = Duration::from_millis(100);
760 assert_eq!(poll_interval.as_millis(), 100);
761 }
762}