1use std::convert::TryInto;
23
24use wasm_bindgen::{prelude::*, JsCast};
25
26use wasm_bindgen_futures::JsFuture;
27
28use futures_util::lock::Mutex;
29use web_sys::UsbDevice;
30
31type Result<T> = std::result::Result<T, Error>;
32
33#[derive(Default, Debug, Copy, Clone)]
35pub struct Ack {
36 pub received: bool,
38 pub power_detector: bool,
40 pub retry: usize,
42 pub length: usize,
44}
45
46pub struct SharedCrazyradio {
48 radio: Mutex<Crazyradio>,
49}
50
51impl SharedCrazyradio {
57 pub fn new(radio: Crazyradio) -> Self {
58 Self {
59 radio: Mutex::new(radio),
60 }
61 }
62
63 pub async fn send_packet_async(
64 &self,
65 channel: Channel,
66 address: [u8; 5],
67 payload: Vec<u8>,
68 ) -> Result<(Ack, Vec<u8>)> {
69 let mut radio = self.radio.lock().await;
70 radio.set_channel_async(channel).await?;
71 radio.set_address_async(&address).await?;
72 radio.send_packet_async(payload.clone()).await
73 }
74
75 pub async fn scan_async(
76 &self,
77 start: Channel,
78 stop: Channel,
79 address: [u8; 5],
80 payload: Vec<u8>,
81 ) -> Result<Vec<Channel>> {
82 let mut found = Vec::new();
83
84 let start: u8 = start.into();
85 let stop: u8 = stop.into();
86
87 for channel in start..=stop {
88 let mut radio = self.radio.lock().await;
89 radio.set_address_async(&address).await?;
90 radio.set_channel_async(channel.try_into()?).await?;
91 let (ack, _) = radio.send_packet_async(payload.clone()).await?;
92 if ack.received {
93 found.push(channel.try_into()?);
94 }
95 }
96
97 Ok(found)
98 }
99}
100
101pub struct Crazyradio {
106 device: web_sys::UsbDevice,
107 current_channel: Option<Channel>,
108 current_address: Option<[u8; 5]>,
109}
110
111const SET_RADIO_CHANNEL: u8 = 0x01;
112const SET_RADIO_ADDRESS: u8 = 0x02;
113
114impl Crazyradio {
115 pub async fn open_first_async() -> Result<Crazyradio> {
126 Self::open_nth_async(0).await
127 }
128
129 pub async fn open_nth_async(nth: usize) -> Result<Crazyradio> {
143 if nth != 0 {
144 return Err(Error::InvalidArgument);
145 }
146
147 let window = web_sys::window().expect("No global 'window' exists!");
148 let navigator: web_sys::Navigator = window.navigator();
149 let usb = navigator.usb();
150
151 let filter: serde_json::Value =
152 serde_json::from_str(r#"{ "filters": [{ "vendorId": 6421 }] }"#).unwrap();
153 let filter = JsValue::from_serde(&filter).unwrap();
154
155 let devices: js_sys::Array = JsFuture::from(usb.get_devices()).await?.into();
156
157 let device: web_sys::UsbDevice = if devices.length() > 0 {
160 devices.get(0).dyn_into().unwrap()
161 } else {
162 JsFuture::from(usb.request_device(&filter.into()))
163 .await?
164 .dyn_into()
165 .map_err(|e| Error::BrowserError(format!("{:?}", e)))?
166 };
167
168 JsFuture::from(device.open()).await?;
169 JsFuture::from(device.claim_interface(0)).await?;
170
171 Ok(Self {
172 device,
173 current_channel: None,
174 current_address: None,
175 })
176 }
177
178 pub async fn open_by_serial_async(serial: &str) -> Result<Crazyradio> {
182 let window = web_sys::window().expect("No global 'window' exists!");
183 let navigator: web_sys::Navigator = window.navigator();
184 let usb = navigator.usb();
185
186 let devices: js_sys::Array = JsFuture::from(usb.get_devices()).await?.into();
187
188 let device: web_sys::UsbDevice = devices
190 .find(&mut |device: JsValue, _, _| {
191 device.dyn_into::<UsbDevice>().unwrap().serial_number() == Some(serial.to_owned())
192 })
193 .dyn_into()
194 .map_err(|_| Error::NotFound)?;
195
196 JsFuture::from(device.open()).await?;
197 JsFuture::from(device.claim_interface(0)).await?;
198
199 Ok(Self {
200 device,
201 current_channel: None,
202 current_address: None,
203 })
204 }
205
206 pub async fn list_serials_async() -> Result<Vec<String>> {
208 let window = web_sys::window().expect("No global 'window' exists!");
209 let navigator: web_sys::Navigator = window.navigator();
210 let usb = navigator.usb();
211
212 let devices: js_sys::Array = JsFuture::from(usb.get_devices()).await?.into();
213
214 let mut serials = Vec::new();
215
216 for device in devices.iter() {
217 let device = device.dyn_into::<web_sys::UsbDevice>().unwrap();
218 if let Some(serial) = device.serial_number() {
219 serials.push(serial);
220 }
221 }
222
223 Ok(serials)
224 }
225
226 async fn set_address_async(&mut self, address: &[u8; 5]) -> Result<()> {
227 if self.current_address != Some(*address) {
228 let parameter = web_sys::UsbControlTransferParameters::new(
229 0,
230 web_sys::UsbRecipient::Device,
231 SET_RADIO_ADDRESS,
232 web_sys::UsbRequestType::Vendor,
233 0,
234 );
235
236 let mut data = *address;
237 let transfer = self
238 .device
239 .control_transfer_out_with_u8_array(¶meter, &mut data);
240
241 let _ = JsFuture::from(transfer)
242 .await?
243 .dyn_into::<web_sys::UsbOutTransferResult>()
244 .unwrap();
245
246 self.current_address = Some(*address);
247 }
248
249 Ok(())
250 }
251
252 async fn set_channel_async(&mut self, channel: Channel) -> Result<()> {
253 if self.current_channel != Some(channel) {
254 let parameter = web_sys::UsbControlTransferParameters::new(
255 0,
256 web_sys::UsbRecipient::Device,
257 SET_RADIO_CHANNEL,
258 web_sys::UsbRequestType::Vendor,
259 channel.into(),
260 );
261
262 let mut data = [];
263 let transfer = self
264 .device
265 .control_transfer_out_with_u8_array(¶meter, &mut data);
266
267 let _ = JsFuture::from(transfer)
268 .await?
269 .dyn_into::<web_sys::UsbOutTransferResult>()
270 .unwrap();
271
272 self.current_channel = Some(channel);
273 }
274
275 Ok(())
276 }
277
278 async fn send_packet_async(&self, packet: Vec<u8>) -> Result<(Ack, Vec<u8>)> {
279 let mut packet = packet;
280 JsFuture::from(self.device.transfer_out_with_u8_array(0x01, &mut packet)).await?;
281
282 let data = JsFuture::from(self.device.transfer_in(0x01, 64))
283 .await?
284 .dyn_into::<web_sys::UsbInTransferResult>()
285 .unwrap();
286
287 let mut pk = Vec::new();
288 for i in 1..data.data().unwrap().byte_length() {
289 pk.push(data.data().unwrap().get_uint8(i));
290 }
291
292 let mut ack = Ack::default();
293 if data.data().unwrap().get_uint8(0) != 0 {
294 ack.received = true;
295 ack.length = pk.len();
296 }
297
298 Ok((ack, pk))
299 }
300}
301
302#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
303#[cfg_attr(feature = "serde_support", derive(Serialize))]
304pub struct Channel(u8);
305
306impl Channel {
307 pub fn from_number(channel: u8) -> Result<Self> {
308 if channel < 126 {
309 Ok(Channel(channel))
310 } else {
311 Err(Error::InvalidArgument)
312 }
313 }
314}
315
316#[allow(clippy::from_over_into)]
318impl Into<u8> for Channel {
319 fn into(self) -> u8 {
320 self.0
321 }
322}
323
324#[allow(clippy::from_over_into)]
326impl Into<u16> for Channel {
327 fn into(self) -> u16 {
328 self.0.into()
329 }
330}
331
332impl TryInto<Channel> for u8 {
333 type Error = Error;
334
335 fn try_into(self) -> std::result::Result<Channel, Self::Error> {
336 Channel::from_number(self)
337 }
338}
339
340#[derive(thiserror::Error, Debug, Clone)]
341pub enum Error {
342 #[error("Crazyradio not found")]
343 NotFound,
344 #[error("Invalid arguments")]
345 InvalidArgument,
346 #[error("Crazyradio version not supported")]
347 DongleVersionNotSupported,
348 #[error("Browser error")]
349 BrowserError(String),
350}
351
352impl From<JsValue> for Error {
353 fn from(e: JsValue) -> Self {
354 Self::BrowserError(format!("{:?}", e))
355 }
356}