1#[allow(unused_imports)]
4use defmt::{debug, error, info, trace, warn};
5use embedded_hal::blocking::delay::DelayMs;
6use embedded_hal::blocking::i2c::{Read, SevenBitAddress, Write};
7use serde::{Deserialize, Serialize};
8
9use super::{str_string, FutureResponse, NoteError, Notecard};
10
11pub struct Card<'a, IOM: Write<SevenBitAddress> + Read<SevenBitAddress>, const BS: usize> {
12 note: &'a mut Notecard<IOM, BS>,
13}
14
15pub enum Transport {
17 Reset,
18 WifiCell,
19 Wifi,
20 Cell,
21 NTN,
22 WifiNTN,
23 CellNTN,
24 WifiCellNTN,
25}
26
27impl Transport {
28 pub fn str(&self) -> &'static str {
29 use Transport::*;
30
31 match self {
32 Reset => "-",
33 WifiCell => "wifi-cell",
34 Wifi => "wifi",
35 Cell => "cell",
36 NTN => "ntn",
37 WifiNTN => "wifi-ntn",
38 CellNTN => "cell-ntn",
39 WifiCellNTN => "wifi-cell-ntn",
40 }
41 }
42}
43
44pub enum GpioMode {
47 Off,
48 Low,
49 High,
50 Input,
51}
52
53impl GpioMode {
54 pub fn str(&self) -> &'static str {
55 use GpioMode::*;
56
57 match self {
58 Off => "off",
59 Low => "low",
60 High => "high",
61 Input => "input",
62 }
63 }
64}
65
66impl<'a, IOM: Write<SevenBitAddress> + Read<SevenBitAddress>, const BS: usize> Card<'a, IOM, BS> {
67 pub fn from(note: &mut Notecard<IOM, BS>) -> Card<'_, IOM, BS> {
68 Card { note }
69 }
70
71 pub fn time(
75 self,
76 delay: &mut impl DelayMs<u16>,
77 ) -> Result<FutureResponse<'a, res::Time, IOM, BS>, NoteError> {
78 self.note.request_raw(delay, b"{\"req\":\"card.time\"}\n")?;
79 Ok(FutureResponse::from(self.note))
80 }
81
82 pub fn status(
84 self,
85 delay: &mut impl DelayMs<u16>,
86 ) -> Result<FutureResponse<'a, res::Status, IOM, BS>, NoteError> {
87 self.note
88 .request_raw(delay, b"{\"req\":\"card.status\"}\n")?;
89 Ok(FutureResponse::from(self.note))
90 }
91
92 pub fn restart(
94 self,
95 delay: &mut impl DelayMs<u16>,
96 ) -> Result<FutureResponse<'a, res::Empty, IOM, BS>, NoteError> {
97 self.note
98 .request_raw(delay, b"{\"req\":\"card.restart\"}\n")?;
99 Ok(FutureResponse::from(self.note))
100 }
101
102 pub fn location(
104 self,
105 delay: &mut impl DelayMs<u16>,
106 ) -> Result<FutureResponse<'a, res::Location, IOM, BS>, NoteError> {
107 self.note
108 .request_raw(delay, b"{\"req\":\"card.location\"}\n")?;
109 Ok(FutureResponse::from(self.note))
110 }
111
112 pub fn location_mode(
114 self,
115 delay: &mut impl DelayMs<u16>,
116 mode: Option<&str>,
117 seconds: Option<u32>,
118 vseconds: Option<&str>,
119 delete: Option<bool>,
120 max: Option<u32>,
121 lat: Option<f32>,
122 lon: Option<f32>,
123 minutes: Option<u32>,
124 ) -> Result<FutureResponse<'a, res::LocationMode, IOM, BS>, NoteError> {
125 self.note.request(
126 delay,
127 req::LocationMode {
128 req: "card.location.mode",
129 mode: str_string(mode)?,
130 seconds,
131 vseconds: str_string(vseconds)?,
132 delete,
133 max,
134 lat,
135 lon,
136 minutes,
137 },
138 )?;
139 Ok(FutureResponse::from(self.note))
140 }
141
142 pub fn location_track(
145 self,
146 delay: &mut impl DelayMs<u16>,
147 start: bool,
148 heartbeat: bool,
149 sync: bool,
150 hours: Option<i32>,
151 file: Option<&str>,
152 ) -> Result<FutureResponse<'a, res::LocationTrack, IOM, BS>, NoteError> {
153 self.note.request(
154 delay,
155 req::LocationTrack {
156 req: "card.location.track",
157 start: start.then_some(true),
158 stop: (!start).then_some(true),
159 heartbeat: heartbeat.then_some(true),
160 sync: sync.then_some(true),
161 hours,
162 file: str_string(file)?,
163 },
164 )?;
165
166 Ok(FutureResponse::from(self.note))
167 }
168
169 pub fn wireless(
170 self,
171 delay: &mut impl DelayMs<u16>,
172 mode: Option<&str>,
173 apn: Option<&str>,
174 method: Option<&str>,
175 hours: Option<u32>,
176 ) -> Result<FutureResponse<'a, res::Wireless, IOM, BS>, NoteError> {
177 self.note.request(
178 delay,
179 req::Wireless {
180 req: "card.wireless",
181 mode: str_string(mode)?,
182 method: str_string(method)?,
183 apn: str_string(apn)?,
184 hours,
185 },
186 )?;
187
188 Ok(FutureResponse::from(self.note))
189 }
190
191 pub fn version(
193 self,
194 delay: &mut impl DelayMs<u16>,
195 ) -> Result<FutureResponse<'a, res::Version, IOM, BS>, NoteError> {
196 self.note
197 .request_raw(delay, b"{\"req\":\"card.version\"}\n")?;
198 Ok(FutureResponse::from(self.note))
199 }
200
201 pub fn dfu(
204 self,
205 delay: &mut impl DelayMs<u16>,
206 name: Option<req::DFUName>,
207 on: Option<bool>,
208 stop: Option<bool>,
209 ) -> Result<FutureResponse<'a, res::DFU, IOM, BS>, NoteError> {
210 self.note.request(delay, req::DFU::new(name, on, stop))?;
211 Ok(FutureResponse::from(self.note))
212 }
213
214 pub fn transport(
215 self,
216 delay: &mut impl DelayMs<u16>,
217 method: Transport,
218 allow: Option<bool>,
219 umin: Option<bool>,
220 seconds: Option<u32>,
221 ) -> Result<FutureResponse<'a, res::Transport, IOM, BS>, NoteError> {
222 self.note.request(
223 delay,
224 req::Transport {
225 req: "card.transport",
226 method: method.str(),
227 allow,
228 umin,
229 seconds,
230 },
231 )?;
232 Ok(FutureResponse::from(self.note))
233 }
234
235 pub fn aux_off(
239 self,
240 delay: &mut impl DelayMs<u16>,
241 ) -> Result<FutureResponse<'a, res::Aux, IOM, BS>, NoteError> {
242 self.note.request_raw(delay, b"{\"req\":\"card.aux\", \"mode\":\"off\"}\n")?;
243 Ok(FutureResponse::from(self.note))
244 }
245
246 pub fn aux_gpio(
250 self,
251 delay: &mut impl DelayMs<u16>,
252 aux1: GpioMode,
253 aux2: GpioMode,
254 aux3: GpioMode,
255 aux4: GpioMode,
256 ) -> Result<FutureResponse<'a, res::Aux, IOM, BS>, NoteError> {
257 self.note.request(
258 delay,
259 req::Aux {
260 req: "card.aux",
261 mode: "gpio",
262 usage: [aux1.str(), aux2.str(), aux3.str(), aux4.str()],
263 },
264 )?;
265 Ok(FutureResponse::from(self.note))
266 }
267}
268
269pub mod req {
270
271 use super::*;
272
273 #[derive(Deserialize, Serialize, Debug, defmt::Format, Default)]
274 pub struct Aux {
275 pub req: &'static str,
276 pub mode: &'static str,
277 pub usage: [&'static str; 4],
278 }
279
280 #[derive(Deserialize, Serialize, Debug, defmt::Format, Default)]
281 pub struct Transport {
282 pub req: &'static str,
283
284 pub method: &'static str,
285
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub allow: Option<bool>,
288
289 #[serde(skip_serializing_if = "Option::is_none")]
290 pub umin: Option<bool>,
291
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub seconds: Option<u32>,
295 }
296
297 #[derive(Deserialize, Serialize, Debug, defmt::Format, Default)]
298 pub struct Wireless {
299 pub req: &'static str,
300
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub mode: Option<heapless::String<20>>,
303
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub apn: Option<heapless::String<120>>,
306
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub method: Option<heapless::String<120>>,
309
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub hours: Option<u32>,
312 }
313
314 #[derive(Deserialize, Serialize, Debug, defmt::Format, Default)]
315 pub struct LocationTrack {
316 pub req: &'static str,
317
318 #[serde(skip_serializing_if = "Option::is_none")]
319 pub start: Option<bool>,
320
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub heartbeat: Option<bool>,
323
324 #[serde(skip_serializing_if = "Option::is_none")]
325 pub sync: Option<bool>,
326
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub stop: Option<bool>,
329
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub hours: Option<i32>,
332
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub file: Option<heapless::String<20>>,
335 }
336
337 #[derive(Deserialize, Serialize, Debug, defmt::Format, Default)]
338 pub struct LocationMode {
339 pub req: &'static str,
340
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub mode: Option<heapless::String<20>>,
343
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub seconds: Option<u32>,
346
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub vseconds: Option<heapless::String<20>>,
349
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub delete: Option<bool>,
352
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub max: Option<u32>,
355
356 #[serde(skip_serializing_if = "Option::is_none")]
357 pub lat: Option<f32>,
358
359 #[serde(skip_serializing_if = "Option::is_none")]
360 pub lon: Option<f32>,
361
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub minutes: Option<u32>,
364 }
365
366 #[derive(Deserialize, Serialize, Debug, defmt::Format, PartialEq)]
367 #[serde(rename_all = "lowercase")]
368 pub enum DFUName {
369 Esp32,
370 Stm32,
371 #[serde(rename = "stm32-bi")]
372 Stm32Bi,
373 McuBoot,
374 #[serde(rename = "-")]
375 Reset,
376 }
377
378 #[derive(Deserialize, Serialize, Debug, defmt::Format)]
379 pub struct DFU {
380 pub req: &'static str,
381
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub name: Option<req::DFUName>,
384
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub on: Option<bool>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub off: Option<bool>,
390
391 #[serde(skip_serializing_if = "Option::is_none")]
392 pub stop: Option<bool>,
393
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub start: Option<bool>,
396 }
397
398 impl DFU {
399 pub fn new(name: Option<req::DFUName>, on: Option<bool>, stop: Option<bool>) -> Self {
400 Self {
406 req: "card.dfu",
407 name,
408 on: on.and_then(|v| if v { Some(true) } else { None }),
409 off: on.and_then(|v| if v { None } else { Some(true) }),
410 stop: stop.and_then(|v| if v { Some(true) } else { None }),
411 start: stop.and_then(|v| if v { None } else { Some(true) }),
412 }
413 }
414 }
415}
416
417pub mod res {
418 use super::*;
419
420 #[derive(Deserialize, Debug, defmt::Format)]
421 pub struct Empty {}
422
423 #[derive(Deserialize, Debug, defmt::Format)]
424 pub struct LocationTrack {
425 pub start: Option<bool>,
426 pub stop: Option<bool>,
427 pub heartbeat: Option<bool>,
428 pub seconds: Option<u32>,
429 pub hours: Option<i32>,
430 pub file: Option<heapless::String<20>>,
431 }
432
433 #[derive(Deserialize, Debug, defmt::Format)]
434 pub struct Aux {
435 pub mode: Option<heapless::String<20>>,
436 pub power: Option<bool>,
437 pub seconds: Option<u32>,
438 pub time: Option<u32>,
439 pub state: Option<[GpioState; 4]>,
440 }
441
442 #[derive(Deserialize, Debug, defmt::Format)]
443 pub struct GpioState {
444 pub low: Option<bool>,
445 pub high: Option<bool>,
446 pub input: Option<bool>,
447 pub count: Option<heapless::Vec<u32, 128>>,
448 }
449
450 #[derive(Deserialize, Debug, defmt::Format)]
451 pub struct LocationMode {
452 pub mode: heapless::String<60>,
453 pub seconds: Option<u32>,
454 pub vseconds: Option<heapless::String<40>>,
455 pub max: Option<u32>,
456 pub lat: Option<f64>,
457 pub lon: Option<f64>,
458 pub minutes: Option<u32>,
459 }
460
461 #[derive(Deserialize, Debug, defmt::Format)]
462 pub struct Location {
463 pub status: heapless::String<120>,
464 pub mode: heapless::String<120>,
465 pub lat: Option<f64>,
466 pub lon: Option<f64>,
467 pub time: Option<u32>,
468 pub max: Option<u32>,
469 }
470
471 #[derive(Deserialize, Debug, defmt::Format)]
472 pub struct Time {
473 pub time: Option<u32>,
474 pub area: Option<heapless::String<120>>,
475 pub zone: Option<heapless::String<120>>,
476 pub minutes: Option<i32>,
477 pub lat: Option<f64>,
478 pub lon: Option<f64>,
479 pub country: Option<heapless::String<120>>,
480 }
481
482 #[derive(Deserialize, Debug, defmt::Format)]
483 pub struct Status {
484 pub status: heapless::String<40>,
485 #[serde(default)]
486 pub usb: bool,
487 pub storage: usize,
488 pub time: Option<u64>,
489 #[serde(default)]
490 pub connected: bool,
491 }
492
493 #[derive(Deserialize, Debug, defmt::Format)]
494 pub struct WirelessNet {
495 iccid: Option<heapless::String<24>>,
496 imsi: Option<heapless::String<24>>,
497 imei: Option<heapless::String<24>>,
498 modem: Option<heapless::String<35>>,
499 band: Option<heapless::String<24>>,
500 rat: Option<heapless::String<24>>,
501 ratr: Option<heapless::String<24>>,
502 internal: Option<bool>,
503 rssir: Option<i32>,
504 rssi: Option<i32>,
505 rsrp: Option<i32>,
506 sinr: Option<i32>,
507 rsrq: Option<i32>,
508 bars: Option<i32>,
509 mcc: Option<i32>,
510 mnc: Option<i32>,
511 lac: Option<i32>,
512 cid: Option<i32>,
513 modem_temp: Option<i32>,
514 updated: Option<u32>,
515 }
516
517 #[derive(Deserialize, Debug, defmt::Format)]
518 pub struct Wireless {
519 pub status: Option<heapless::String<24>>,
520 pub mode: Option<heapless::String<24>>,
521 pub count: Option<u8>,
522 pub net: Option<WirelessNet>,
523 }
524
525 #[derive(Deserialize, Debug, defmt::Format)]
526 pub struct VersionInner {
527 pub org: heapless::String<24>,
528 pub product: heapless::String<24>,
529 pub version: heapless::String<24>,
530 pub ver_major: u8,
531 pub ver_minor: u8,
532 pub ver_patch: u8,
533 pub ver_build: u32,
534 pub built: heapless::String<24>,
535 pub target: Option<heapless::String<5>>,
536 }
537
538 #[derive(Deserialize, Debug, defmt::Format)]
539 pub struct Version {
540 pub body: VersionInner,
541 pub version: heapless::String<24>,
542 pub device: heapless::String<24>,
543 pub name: heapless::String<30>,
544 pub board: heapless::String<24>,
545 pub sku: heapless::String<24>,
546 pub api: Option<u16>,
547 pub wifi: Option<bool>,
548 pub cell: Option<bool>,
549 pub gps: Option<bool>,
550 pub ordering_code: Option<heapless::String<50>>,
551 }
552
553 #[derive(Deserialize, Debug, defmt::Format)]
554 pub struct DFU {
555 pub name: req::DFUName,
556 }
557
558 #[derive(Deserialize, Debug, defmt::Format)]
559 pub struct Transport {
560 pub method: heapless::String<120>,
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 use super::*;
567 use crate::NotecardError;
568
569 #[test]
570 fn test_version() {
571 let r = br##"{
572 "body": {
573 "org": "Blues Wireless",
574 "product": "Notecard",
575 "version": "notecard-1.5.0",
576 "ver_major": 1,
577 "ver_minor": 5,
578 "ver_patch": 0,
579 "ver_build": 11236,
580 "built": "Sep 2 2020 08:45:10"
581 },
582 "version": "notecard-1.5.0.11236",
583 "device": "dev:000000000000000",
584 "name": "Blues Wireless Notecard",
585 "board": "1.11",
586 "sku": "NOTE-WBNA500",
587 "api": 1
588}"##;
589 let d = &mut serde_json::Deserializer::from_slice(r);
590 serde_path_to_error::deserialize::<_, res::Version>(d).unwrap();
591 }
592
593 #[test]
594 fn test_version_411() {
595 let r = br##"{"version":"notecard-4.1.1.4015681","device":"dev:000000000000000","name":"Blues Wireless Notecard","sku":"NOTE-WBEX-500","board":"1.11","api":4,"body":{"org":"Blues Wireless","product":"Notecard","version":"notecard-4.1.1","ver_major":4,"ver_minor":1,"ver_patch":1,"ver_build":4015681,"built":"Dec 5 2022 12:54:58"}}"##;
596 let d = &mut serde_json::Deserializer::from_slice(r);
597 serde_path_to_error::deserialize::<_, res::Version>(d).unwrap();
598 }
599
600 #[test]
601 fn test_version_813() {
602 let r = br##"{"version":"notecard-8.1.3.17044","device":"dev:000000000000000","name":"Blues Wireless Notecard","sku":"NOTE-WBNAN","ordering_code":"EA0WT1N0AXBB","board":"5.13","cell":true,"gps":true,"body":{"org":"Blues Wireless","product":"Notecard","target":"u5","version":"notecard-u5-8.1.3","ver_major":8,"ver_minor":1,"ver_patch":3,"ver_build":17044,"built":"Dec 20 2024 08:45:13"}}"##;
603 let d = &mut serde_json::Deserializer::from_slice(r);
604 serde_path_to_error::deserialize::<_, res::Version>(d).unwrap();
605 }
606
607 #[test]
608 fn test_version_752() {
609 let r = br##"{"version":"notecard-7.5.2.17004","device":"dev:861059067974133","name":"Blues Wireless Notecard","sku":"NOTE-NBGLN","ordering_code":"EB0WT1N0AXBA","board":"5.13","cell":true,"gps":true,"body":{"org":"Blues Wireless","product":"Notecard","target":"u5","version":"notecard-u5-7.5.2","ver_major":7,"ver_minor":5,"ver_patch":2,"ver_build":17004,"built":"Nov 26 2024 14:01:26"}}"##;
610 let d = &mut serde_json::Deserializer::from_slice(r);
611 serde_path_to_error::deserialize::<_, res::Version>(d).unwrap();
612 }
613
614 #[test]
615 fn test_card_wireless() {
616 let r = br##"{"status":"{modem-on}","count":3,"net":{"iccid":"89011703278520607527","imsi":"310170852060752","imei":"864475044204278","modem":"BG95M3LAR02A03_01.006.01.006","band":"GSM 900","rat":"gsm","rssir":-77,"rssi":-77,"bars":3,"mcc":242,"mnc":1,"lac":11001,"cid":12313,"updated":1643923524}}"##;
617 let d = &mut serde_json::Deserializer::from_slice(r);
618 serde_path_to_error::deserialize::<_, res::Wireless>(d).unwrap();
619
620 let r = br##"{"status":"{cell-registration-wait}","net":{"iccid":"89011703278520606586","imsi":"310170852060658","imei":"864475044197092","modem":"BG95M3LAR02A03_01.006.01.006"}}"##;
621 let d = &mut serde_json::Deserializer::from_slice(r);
622 serde_path_to_error::deserialize::<_, res::Wireless>(d).unwrap();
623
624 let r = br##"{"status":"{modem-off}","net":{}}"##;
625 let d = &mut serde_json::Deserializer::from_slice(r);
626 serde_path_to_error::deserialize::<_, res::Wireless>(d).unwrap();
627
628 let r = br##"{"status":"{network-up}","mode":"auto","count":3,"net":{"iccid":"89011703278520578660","imsi":"310170852057866","imei":"867730051260788","modem":"BG95M3LAR02A03_01.006.01.006","band":"GSM 900","rat":"gsm","rssir":-77,"rssi":-78,"bars":3,"mcc":242,"mnc":1,"lac":11,"cid":12286,"updated":1646227929}}"##;
629 let d = &mut serde_json::Deserializer::from_slice(r);
630 serde_path_to_error::deserialize::<_, res::Wireless>(d).unwrap();
631
632 let r = br##"{"mode":"auto","count":2,"net":{"iccid":"89011704278930030582","imsi":"310170893003058","imei":"860264054655247","modem":"EG91EXGAR08A05M1G_01.001.01.001","band":"LTE BAND 20","rat":"lte","ratr":"\"LTE\"","internal":true,"rssir":-59,"rssi":-60,"rsrp":-92,"sinr":15,"rsrq":-9,"bars":2,"mcc":242,"mnc":2,"lac":2501,"cid":35398693,"modem_temp":34,"updated":1746004605}}"##;
634 let d = &mut serde_json::Deserializer::from_slice(r);
635 serde_path_to_error::deserialize::<_, res::Wireless>(d).unwrap();
636 }
637
638 #[test]
639 fn test_card_time_ok() {
640 let r = br##"
641 {
642 "time": 1599769214,
643 "area": "Beverly, MA",
644 "zone": "CDT,America/New York",
645 "minutes": -300,
646 "lat": 42.5776,
647 "lon": -70.87134,
648 "country": "US"
649 }
650 "##;
651
652 let d = &mut serde_json::Deserializer::from_slice(r);
653 serde_path_to_error::deserialize::<_, res::Time>(d).unwrap();
654 }
655
656 #[test]
657 fn test_card_time_sa() {
658 let r = br##"
659 {
660 "time": 1599769214,
661 "area": "Kommetjie Western Cape",
662 "zone": "Africa/Johannesburg",
663 "minutes": -300,
664 "lat": 42.5776,
665 "lon": -70.87134,
666 "country": "ZA"
667 }
668 "##;
669
670 let d = &mut serde_json::Deserializer::from_slice(r);
671 serde_path_to_error::deserialize::<_, res::Time>(d).unwrap();
672 }
673
674 #[test]
675 fn test_card_time_err() {
676 let r = br##"{"err":"time is not yet set","zone":"UTC,Unknown"}"##;
677 let d = &mut serde_json::Deserializer::from_slice(r);
678 serde_path_to_error::deserialize::<_, NotecardError>(d).unwrap();
679 }
680
681 #[test]
682 pub fn test_status_ok() {
683 let d = &mut serde_json::Deserializer::from_str(
684 r#"
685 {
686 "status": "{normal}",
687 "usb": true,
688 "storage": 8,
689 "time": 1599684765,
690 "connected": true
691 }"#,
692 );
693 serde_path_to_error::deserialize::<_, res::Status>(d).unwrap();
694 }
695
696 #[test]
697 pub fn test_status_mising() {
698 let d = &mut serde_json::Deserializer::from_str(
699 r#"
700 {
701 "status": "{normal}",
702 "usb": true,
703 "storage": 8
704 }"#,
705 );
706
707 serde_path_to_error::deserialize::<_, res::Status>(d).unwrap();
708 }
709
710 #[test]
711 fn test_partial_location_mode() {
712 let d = &mut serde_json::Deserializer::from_str(r#"{"seconds":60,"mode":"periodic"}"#);
713
714 serde_path_to_error::deserialize::<_, res::LocationMode>(d).unwrap();
715 }
716
717 #[test]
718 fn test_parse_exceed_string_size() {
719 let d = &mut serde_json::Deserializer::from_str(r#"{"seconds":60,"mode":"periodicperiodicperiodicperiodicperiodicperiodicperiodic"}"#);
720 serde_path_to_error::deserialize::<_, res::LocationMode>(d)
721 .ok();
722 }
723
724 #[test]
725 fn test_location_searching() {
726 let d = &mut serde_json::Deserializer::from_str(
727 r#"{"status":"GPS search (111 sec, 32/33 dB SNR, 0/1 sats) {gps-active} {gps-signal} {gps-sats}","mode":"continuous"}"#);
728 serde_path_to_error::deserialize::<_, res::Location>(d).unwrap();
729 }
730
731 #[test]
732 fn test_location_mode_err() {
733 let r = br##"{"err":"seconds: field seconds: unmarshal: expected a int32 {io}"}"##;
734 let d = &mut serde_json::Deserializer::from_slice(r);
735 serde_path_to_error::deserialize::<_, NotecardError>(d).unwrap();
736 }
737
738 #[test]
739 fn test_dfu_name() {
740 let (res, _) = serde_json_core::from_str::<req::DFUName>(r#""esp32""#).unwrap();
741 assert_eq!(res, req::DFUName::Esp32);
742 let (res, _) = serde_json_core::from_str::<req::DFUName>(r#""stm32""#).unwrap();
743 assert_eq!(res, req::DFUName::Stm32);
744 let (res, _) = serde_json_core::from_str::<req::DFUName>(r#""stm32-bi""#).unwrap();
745 assert_eq!(res, req::DFUName::Stm32Bi);
746 let (res, _) = serde_json_core::from_str::<req::DFUName>(r#""mcuboot""#).unwrap();
747 assert_eq!(res, req::DFUName::McuBoot);
748 let (res, _) = serde_json_core::from_str::<req::DFUName>(r#""-""#).unwrap();
749 assert_eq!(res, req::DFUName::Reset);
750 }
751
752 #[test]
753 fn test_dfu_req() {
754 let req = req::DFU::new(None, None, None);
756 let res: heapless::String<1024> = serde_json_core::to_string(&req).unwrap();
757 assert_eq!(res, r#"{"req":"card.dfu"}"#);
758
759 let req = req::DFU::new(Some(req::DFUName::Esp32), Some(true), None);
761 let res: heapless::String<256> = serde_json_core::to_string(&req).unwrap();
762 assert_eq!(res, r#"{"req":"card.dfu","name":"esp32","on":true}"#);
763
764 let req = req::DFU::new(None, Some(false), None);
766 let res: heapless::String<256> = serde_json_core::to_string(&req).unwrap();
767 assert_eq!(res, r#"{"req":"card.dfu","off":true}"#);
768
769 let req = req::DFU::new(None, None, Some(true));
771 let res: heapless::String<256> = serde_json_core::to_string(&req).unwrap();
772 assert_eq!(res, r#"{"req":"card.dfu","stop":true}"#);
773
774 let req = req::DFU::new(None, None, Some(false));
776 let res: heapless::String<256> = serde_json_core::to_string(&req).unwrap();
777 assert_eq!(res, r#"{"req":"card.dfu","start":true}"#);
778 }
779
780 #[test]
781 fn test_dfu_res() {
782 let d = &mut serde_json::Deserializer::from_str(r#"{"name": "stm32"}"#);
783 serde_path_to_error::deserialize::<_, res::DFU>(d).unwrap();
784 }
785
786 #[test]
787 fn test_parse_aux_gpio_state() {
788 let d = &mut serde_json::Deserializer::from_str(r#"{
789 "mode": "gpio",
790 "state": [
791 {},
792 {
793 "low": true
794 },
795 {
796 "high": true
797 },
798 {
799 "count": [
800 3
801 ]
802 }
803 ],
804 "time": 1592587637,
805 "seconds": 2
806}"#);
807 serde_path_to_error::deserialize::<_, res::Aux>(d).unwrap();
808 }
809}