Skip to main content

meshcore_rs/commands/
base.rs

1//! Base command handler implementation
2
3use std::collections::HashMap;
4use std::sync::Arc;
5use std::time::Duration;
6use tokio::sync::mpsc;
7
8use crate::events::*;
9use crate::packets::BinaryReqType;
10use crate::parsing::{hex_decode, hex_encode, to_microdegrees};
11use crate::reader::MessageReader;
12use crate::{Error, Result, CHANNEL_NAME_LEN, CHANNEL_SECRET_LEN};
13
14/// Default command timeout
15pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
16
17// Command byte constants (from MeshCore firmware)
18const CMD_APP_START: u8 = 1;
19const CMD_SEND_TXT_MSG: u8 = 2;
20const CMD_SEND_CHANNEL_TXT_MSG: u8 = 3;
21const CMD_GET_CONTACTS: u8 = 4;
22const CMD_GET_DEVICE_TIME: u8 = 5;
23const CMD_SET_DEVICE_TIME: u8 = 6;
24const CMD_SEND_SELF_ADVERT: u8 = 7;
25const CMD_SET_ADVERT_NAME: u8 = 8;
26const CMD_ADD_UPDATE_CONTACT: u8 = 9;
27const CMD_SYNC_NEXT_MESSAGE: u8 = 10;
28#[allow(dead_code)]
29const CMD_SET_RADIO_PARAMS: u8 = 11;
30const CMD_SET_RADIO_TX_POWER: u8 = 12;
31#[allow(dead_code)]
32const CMD_RESET_PATH: u8 = 13;
33const CMD_SET_ADVERT_LATLON: u8 = 14;
34const CMD_REMOVE_CONTACT: u8 = 15;
35#[allow(dead_code)]
36const CMD_SHARE_CONTACT: u8 = 16;
37const CMD_EXPORT_CONTACT: u8 = 17;
38const CMD_IMPORT_CONTACT: u8 = 18;
39const CMD_REBOOT: u8 = 19;
40const CMD_GET_BATT_AND_STORAGE: u8 = 20;
41#[allow(dead_code)]
42const CMD_SET_TUNING_PARAMS: u8 = 21;
43const CMD_DEVICE_QUERY: u8 = 22;
44const CMD_EXPORT_PRIVATE_KEY: u8 = 23;
45const CMD_IMPORT_PRIVATE_KEY: u8 = 24;
46#[allow(dead_code)]
47const CMD_SEND_RAW_DATA: u8 = 25;
48const CMD_SEND_LOGIN: u8 = 26;
49#[allow(dead_code)]
50const CMD_SEND_STATUS_REQ: u8 = 27;
51#[allow(dead_code)]
52const CMD_HAS_CONNECTION: u8 = 28;
53const CMD_LOGOUT: u8 = 29;
54#[allow(dead_code)]
55const CMD_GET_CONTACT_BY_KEY: u8 = 30;
56const CMD_GET_CHANNEL: u8 = 31;
57const CMD_SET_CHANNEL: u8 = 32;
58const CMD_SIGN_START: u8 = 33;
59const CMD_SIGN_DATA: u8 = 34;
60const CMD_SIGN_FINISH: u8 = 35;
61const CMD_GET_CUSTOM_VARS: u8 = 40;
62const CMD_SET_CUSTOM_VAR: u8 = 41;
63const CMD_SEND_BINARY_REQ: u8 = 50;
64const CMD_SET_FLOOD_SCOPE: u8 = 54;
65
66/// Destination type for commands
67#[derive(Debug, Clone)]
68pub enum Destination {
69    /// Raw bytes (6 or 32 bytes)
70    Bytes(Vec<u8>),
71    /// Hex string
72    Hex(String),
73    /// Contact reference
74    Contact(Contact),
75}
76
77impl Destination {
78    /// Get the 6-byte prefix
79    pub fn prefix(&self) -> Result<[u8; 6]> {
80        match self {
81            Destination::Bytes(b) => {
82                if b.len() >= 6 {
83                    let mut prefix = [0u8; 6];
84                    prefix.copy_from_slice(&b[..6]);
85                    Ok(prefix)
86                } else {
87                    Err(Error::invalid_param("Destination too short"))
88                }
89            }
90            Destination::Hex(s) => {
91                let bytes = hex_decode(s)?;
92                if bytes.len() >= 6 {
93                    let mut prefix = [0u8; 6];
94                    prefix.copy_from_slice(&bytes[..6]);
95                    Ok(prefix)
96                } else {
97                    Err(Error::invalid_param("Destination too short"))
98                }
99            }
100            Destination::Contact(c) => Ok(c.prefix()),
101        }
102    }
103
104    /// Get the full public key if available (32 bytes)
105    pub fn public_key(&self) -> Option<[u8; 32]> {
106        match self {
107            Destination::Bytes(b) if b.len() >= 32 => {
108                let mut key = [0u8; 32];
109                key.copy_from_slice(&b[..32]);
110                Some(key)
111            }
112            Destination::Hex(s) => {
113                let bytes = hex_decode(s).ok()?;
114                if bytes.len() >= 32 {
115                    let mut key = [0u8; 32];
116                    key.copy_from_slice(&bytes[..32]);
117                    Some(key)
118                } else {
119                    None
120                }
121            }
122            Destination::Contact(c) => Some(c.public_key),
123            _ => None,
124        }
125    }
126}
127
128impl From<&[u8]> for Destination {
129    fn from(bytes: &[u8]) -> Self {
130        Destination::Bytes(bytes.to_vec())
131    }
132}
133
134impl From<Vec<u8>> for Destination {
135    fn from(bytes: Vec<u8>) -> Self {
136        Destination::Bytes(bytes)
137    }
138}
139
140impl From<&str> for Destination {
141    fn from(s: &str) -> Self {
142        Destination::Hex(s.to_string())
143    }
144}
145
146impl From<String> for Destination {
147    fn from(s: String) -> Self {
148        Destination::Hex(s)
149    }
150}
151
152impl From<Contact> for Destination {
153    fn from(c: Contact) -> Self {
154        Destination::Contact(c)
155    }
156}
157
158impl From<&Contact> for Destination {
159    fn from(c: &Contact) -> Self {
160        Destination::Contact(c.clone())
161    }
162}
163
164/// Command handler for MeshCore operations
165pub struct CommandHandler {
166    /// Sender channel for outgoing data
167    sender: mpsc::Sender<Vec<u8>>,
168    /// Event dispatcher for receiving responses
169    dispatcher: Arc<EventDispatcher>,
170    /// Message reader for binary request tracking
171    reader: Arc<MessageReader>,
172    /// Default timeout for commands
173    default_timeout: Duration,
174}
175
176impl CommandHandler {
177    /// Create a new command handler
178    pub fn new(
179        sender: mpsc::Sender<Vec<u8>>,
180        dispatcher: Arc<EventDispatcher>,
181        reader: Arc<MessageReader>,
182    ) -> Self {
183        Self {
184            sender,
185            dispatcher,
186            reader,
187            default_timeout: DEFAULT_TIMEOUT,
188        }
189    }
190
191    /// Set the default timeout for commands
192    pub fn set_default_timeout(&mut self, timeout: Duration) {
193        self.default_timeout = timeout;
194    }
195
196    /// Send raw data and wait for a response
197    pub async fn send(
198        &self,
199        data: &[u8],
200        expected_event: Option<EventType>,
201    ) -> Result<MeshCoreEvent> {
202        self.send_with_timeout(data, expected_event, self.default_timeout)
203            .await
204    }
205
206    /// Send raw data and wait for a response with the custom timeout
207    pub async fn send_with_timeout(
208        &self,
209        data: &[u8],
210        expected_event: Option<EventType>,
211        timeout: Duration,
212    ) -> Result<MeshCoreEvent> {
213        // Send the data
214        self.sender
215            .send(data.to_vec())
216            .await
217            .map_err(|e| Error::Channel(e.to_string()))?;
218
219        // Wait for response
220        self.wait_for_event(expected_event, HashMap::new(), timeout)
221            .await
222    }
223
224    /// Send raw data and wait for one of multiple response types
225    pub async fn send_multi(
226        &self,
227        data: &[u8],
228        expected_events: &[EventType],
229        timeout: Duration,
230    ) -> Result<MeshCoreEvent> {
231        // Send the data
232        self.sender
233            .send(data.to_vec())
234            .await
235            .map_err(|e| Error::Channel(e.to_string()))?;
236
237        // Wait for any of the expected events
238        self.wait_for_any_event(expected_events, timeout).await
239    }
240
241    /// Wait for a specific event
242    pub async fn wait_for_event(
243        &self,
244        event_type: Option<EventType>,
245        filters: HashMap<String, String>,
246        timeout: Duration,
247    ) -> Result<MeshCoreEvent> {
248        self.dispatcher
249            .wait_for_event(event_type, filters, timeout)
250            .await
251            .ok_or_else(|| Error::timeout(format!("{:?}", event_type)))
252    }
253
254    /// Wait for any of the specified events
255    pub async fn wait_for_any_event(
256        &self,
257        event_types: &[EventType],
258        timeout: Duration,
259    ) -> Result<MeshCoreEvent> {
260        let mut rx = self.dispatcher.receiver();
261
262        tokio::select! {
263            _ = tokio::time::sleep(timeout) => {
264                Err(Error::timeout("response"))
265            }
266            result = async {
267                loop {
268                    match rx.recv().await {
269                        Ok(event) => {
270                            if event_types.contains(&event.event_type) {
271                                return Ok(event);
272                            }
273                        }
274                        Err(_) => return Err(Error::Channel("Receiver closed".to_string())),
275                    }
276                }
277            } => result,
278        }
279    }
280
281    // ========== Device Commands ==========
282
283    /// Send APPSTART command to initialise connection
284    ///
285    /// Format: [CMD_APP_START=0x01][reserved: 7 bytes][app_name: "mccli"]
286    pub async fn send_appstart(&self) -> Result<SelfInfo> {
287        // Byte 0: CMD_APP_START (0x01)
288        // Bytes 1-7: reserved (zeros)
289        // Bytes 8+: app name
290        let data = [
291            CMD_APP_START,
292            0x00,
293            0x00,
294            0x00,
295            0x00,
296            0x00,
297            0x00,
298            0x00, // reserved
299            b'm',
300            b'c',
301            b'c',
302            b'l',
303            b'i', // app name TODO review this
304        ];
305        let event = self.send(&data, Some(EventType::SelfInfo)).await?;
306
307        match event.payload {
308            EventPayload::SelfInfo(info) => Ok(info),
309            _ => Err(Error::protocol("Unexpected response to APPSTART")),
310        }
311    }
312
313    /// Query device info
314    ///
315    /// Format: [CMD_DEVICE_QUERY=0x16][protocol_version]
316    pub async fn send_device_query(&self) -> Result<DeviceInfoData> {
317        // Protocol version 8 is the current version
318        let data = [CMD_DEVICE_QUERY, 8];
319        let event = self.send(&data, Some(EventType::DeviceInfo)).await?;
320
321        match event.payload {
322            EventPayload::DeviceInfo(info) => Ok(info),
323            _ => Err(Error::protocol("Unexpected response to device query")),
324        }
325    }
326
327    /// Get battery level and storage info
328    ///
329    /// Format: [CMD_GET_BATT_AND_STORAGE=0x14]
330    pub async fn get_bat(&self) -> Result<BatteryInfo> {
331        let data = [CMD_GET_BATT_AND_STORAGE];
332        let event = self.send(&data, Some(EventType::Battery)).await?;
333
334        match event.payload {
335            EventPayload::Battery(info) => Ok(info),
336            _ => Err(Error::protocol("Unexpected response to battery query")),
337        }
338    }
339
340    /// Get device time
341    ///
342    /// Format: [CMD_GET_DEVICE_TIME=0x05]
343    pub async fn get_time(&self) -> Result<u32> {
344        let data = [CMD_GET_DEVICE_TIME];
345        let event = self.send(&data, Some(EventType::CurrentTime)).await?;
346
347        match event.payload {
348            EventPayload::Time(t) => Ok(t),
349            _ => Err(Error::protocol("Unexpected response to time query")),
350        }
351    }
352
353    /// Set device time
354    ///
355    /// Format: [CMD_SET_DEVICE_TIME=0x06][timestamp: u32]
356    pub async fn set_time(&self, timestamp: u32) -> Result<MeshCoreEvent> {
357        let mut data = vec![CMD_SET_DEVICE_TIME];
358        data.extend_from_slice(&timestamp.to_le_bytes());
359        self.send(&data, Some(EventType::Ok)).await
360    }
361
362    /// Set the device name
363    ///
364    /// Format: [CMD_SET_ADVERT_NAME=0x08][name]
365    pub async fn set_name(&self, name: &str) -> Result<MeshCoreEvent> {
366        let mut data = vec![CMD_SET_ADVERT_NAME];
367        data.extend_from_slice(name.as_bytes());
368        self.send(&data, Some(EventType::Ok)).await
369    }
370
371    /// Set device coordinates
372    ///
373    /// Format: [CMD_SET_ADVERT_LATLON=0x0E][lat: i32][lon: i32][alt: i32]
374    pub async fn set_coords(&self, lat: f64, lon: f64) -> Result<MeshCoreEvent> {
375        let lat_micro = to_microdegrees(lat);
376        let lon_micro = to_microdegrees(lon);
377
378        let mut data = vec![CMD_SET_ADVERT_LATLON];
379        data.extend_from_slice(&lat_micro.to_le_bytes());
380        data.extend_from_slice(&lon_micro.to_le_bytes());
381        // Alt is optional, firmware handles len >= 9
382        self.send(&data, Some(EventType::Ok)).await
383    }
384
385    /// Set TX power
386    ///
387    /// Format: [CMD_SET_RADIO_TX_POWER=0x0C][power: u8]
388    pub async fn set_tx_power(&self, power: u8) -> Result<MeshCoreEvent> {
389        let data = [CMD_SET_RADIO_TX_POWER, power];
390        self.send(&data, Some(EventType::Ok)).await
391    }
392
393    /// Send advertisement
394    ///
395    /// Format: [CMD_SEND_SELF_ADVERT=0x07][flood: optional]
396    pub async fn send_advert(&self, flood: bool) -> Result<MeshCoreEvent> {
397        let data = if flood {
398            vec![CMD_SEND_SELF_ADVERT, 0x01]
399        } else {
400            vec![CMD_SEND_SELF_ADVERT]
401        };
402        self.send(&data, Some(EventType::Ok)).await
403    }
404
405    /// Reboot device (no response expected)
406    ///
407    /// Format: [CMD_REBOOT=0x13]["reboot"]
408    pub async fn reboot(&self) -> Result<()> {
409        let data = [CMD_REBOOT, b'r', b'e', b'b', b'o', b'o', b't'];
410        self.sender
411            .send(data.to_vec())
412            .await
413            .map_err(|e| Error::Channel(e.to_string()))
414    }
415
416    /// Get custom variables
417    ///
418    /// Format: [CMD_GET_CUSTOM_VARS=0x28]
419    pub async fn get_custom_vars(&self) -> Result<HashMap<String, String>> {
420        let data = [CMD_GET_CUSTOM_VARS];
421        let event = self.send(&data, Some(EventType::CustomVars)).await?;
422
423        match event.payload {
424            EventPayload::CustomVars(vars) => Ok(vars),
425            _ => Err(Error::protocol("Unexpected response to custom vars query")),
426        }
427    }
428
429    /// Set a custom variable
430    ///
431    /// Format: [CMD_SET_CUSTOM_VAR=0x29][key=value]
432    pub async fn set_custom_var(&self, key: &str, value: &str) -> Result<()> {
433        let mut data = vec![CMD_SET_CUSTOM_VAR];
434        data.extend_from_slice(key.as_bytes());
435        data.push(b'=');
436        data.extend_from_slice(value.as_bytes());
437        self.send(&data, Some(EventType::Ok)).await?;
438        Ok(())
439    }
440
441    /// Get channel info
442    ///
443    /// Format: [CMD_GET_CHANNEL=0x1F][channel_idx: u8]
444    pub async fn get_channel(&self, channel_idx: u8) -> Result<ChannelInfoData> {
445        let data = [CMD_GET_CHANNEL, channel_idx];
446        let event = self.send(&data, Some(EventType::ChannelInfo)).await?;
447
448        match event.payload {
449            EventPayload::ChannelInfo(info) => Ok(info),
450            _ => Err(Error::protocol("Unexpected response to channel query")),
451        }
452    }
453
454    /// Set channel
455    ///
456    /// Format: [CMD_SET_CHANNEL=0x20][channel_idx][name: CHANNEL_NAME_LEN bytes][secret: CHANNEL_SECRET_LEN bytes]
457    /// Note: name is null-terminated, so max usable length is CHANNEL_NAME_LEN - 1 bytes
458    pub async fn set_channel(
459        &self,
460        channel_idx: u8,
461        name: &str,
462        secret: &[u8; CHANNEL_SECRET_LEN],
463    ) -> Result<()> {
464        let mut data = vec![CMD_SET_CHANNEL, channel_idx];
465        // Pad or truncate name to CHANNEL_NAME_LEN bytes, reserving last byte for null terminator
466        let mut name_bytes = [0u8; CHANNEL_NAME_LEN];
467        // Max usable length is CHANNEL_NAME_LEN - 1 to ensure null termination
468        let name_len = name.len().min(CHANNEL_NAME_LEN - 1);
469        name_bytes[..name_len].copy_from_slice(&name.as_bytes()[..name_len]);
470        // name_bytes[name_len..] is already zero (null terminator guaranteed)
471        data.extend_from_slice(&name_bytes);
472        data.extend_from_slice(secret);
473        self.send(&data, Some(EventType::Ok)).await?;
474        Ok(())
475    }
476
477    /// Set flood scope (region code)
478    ///
479    /// Format: [CMD_SET_FLOOD_SCOPE=0x36][0][scope: 16 bytes]
480    /// Format: [CMD_SET_FLOOD_SCOPE=0x36][0] (to reset flood scope)
481    pub async fn set_flood_scope(&self, scope: Option<&str>) -> Result<()> {
482        let mut data = vec![CMD_SET_FLOOD_SCOPE, 0];
483        if let Some(scope) = scope {
484            data.extend_from_slice(scope.as_bytes());
485            data.resize(18, 0u8);
486        }
487        self.send(&data, Some(EventType::Ok)).await?;
488        Ok(())
489    }
490
491    /// Export private key
492    ///
493    /// Format: [CMD_EXPORT_PRIVATE_KEY=0x17]
494    pub async fn export_private_key(&self) -> Result<[u8; 64]> {
495        let data = [CMD_EXPORT_PRIVATE_KEY];
496        let event = self
497            .send_multi(
498                &data,
499                &[EventType::PrivateKey, EventType::Disabled],
500                self.default_timeout,
501            )
502            .await?;
503
504        match event.payload {
505            EventPayload::PrivateKey(key) => Ok(key),
506            EventPayload::String(msg) => Err(Error::Disabled(msg)),
507            _ => Err(Error::protocol("Unexpected response to export private key")),
508        }
509    }
510
511    /// Import private key
512    ///
513    /// Format: [CMD_IMPORT_PRIVATE_KEY=0x18][key: 64 bytes]
514    pub async fn import_private_key(&self, key: &[u8; 64]) -> Result<()> {
515        let mut data = vec![CMD_IMPORT_PRIVATE_KEY];
516        data.extend_from_slice(key);
517        self.send(&data, Some(EventType::Ok)).await?;
518        Ok(())
519    }
520
521    // ========== Contact Commands ==========
522
523    /// Get the contact list
524    pub async fn get_contacts(&self, last_modification_timestamp: u32) -> Result<Vec<Contact>> {
525        self.get_contacts_with_timeout(last_modification_timestamp, self.default_timeout)
526            .await
527    }
528
529    /// Get the contact list with a custom timeout
530    ///
531    /// Format: [CMD_GET_CONTACTS=0x04][last_mod_timestamp: u32]
532    pub async fn get_contacts_with_timeout(
533        &self,
534        last_modification_timestamp: u32,
535        timeout: Duration,
536    ) -> Result<Vec<Contact>> {
537        let mut data = vec![CMD_GET_CONTACTS];
538        data.extend_from_slice(&last_modification_timestamp.to_le_bytes());
539        let event = self
540            .send_with_timeout(&data, Some(EventType::Contacts), timeout)
541            .await?;
542
543        match event.payload {
544            EventPayload::Contacts(contacts) => Ok(contacts),
545            _ => Err(Error::protocol("Unexpected response to get contacts")),
546        }
547    }
548
549    /// Add or update a contact
550    ///
551    /// Format: [CMD_ADD_UPDATE_CONTACT=0x09][pubkey: 32][type: u8][flags: u8][path_len: u8][path: 64][name: 32][timestamp: u32][lat: i32][lon: i32]
552    pub async fn add_contact(&self, contact: &Contact) -> Result<()> {
553        let mut data = vec![CMD_ADD_UPDATE_CONTACT];
554        data.extend_from_slice(&contact.public_key);
555        data.push(contact.contact_type);
556        data.push(contact.flags);
557        data.push(contact.path_len as u8);
558
559        // Pad path to 64 bytes
560        let mut path = [0u8; 64];
561        let path_len = contact.out_path.len().min(64);
562        path[..path_len].copy_from_slice(&contact.out_path[..path_len]);
563        data.extend_from_slice(&path);
564
565        // Pad name to 32 bytes
566        let mut name = [0u8; 32];
567        let name_len = contact.adv_name.len().min(32);
568        name[..name_len].copy_from_slice(&contact.adv_name.as_bytes()[..name_len]);
569        data.extend_from_slice(&name);
570
571        data.extend_from_slice(&contact.last_advert.to_le_bytes());
572        data.extend_from_slice(&contact.adv_lat.to_le_bytes());
573        data.extend_from_slice(&contact.adv_lon.to_le_bytes());
574
575        self.send(&data, Some(EventType::Ok)).await?;
576        Ok(())
577    }
578
579    /// Remove a contact by public key
580    ///
581    /// Format: [CMD_REMOVE_CONTACT=0x0F][pubkey: 32]
582    pub async fn remove_contact(&self, key: impl Into<Destination>) -> Result<()> {
583        let dest: Destination = key.into();
584        let prefix = dest.prefix()?;
585
586        let mut data = vec![CMD_REMOVE_CONTACT];
587        data.extend_from_slice(&prefix);
588        self.send(&data, Some(EventType::Ok)).await?;
589        Ok(())
590    }
591
592    /// Export contact as URI
593    ///
594    /// Format: [CMD_EXPORT_CONTACT=0x11][pubkey: 32 optional]
595    pub async fn export_contact(&self, key: Option<impl Into<Destination>>) -> Result<String> {
596        let data = if let Some(k) = key {
597            let dest: Destination = k.into();
598            let prefix = dest.prefix()?;
599            let mut d = vec![CMD_EXPORT_CONTACT];
600            d.extend_from_slice(&prefix);
601            d
602        } else {
603            vec![CMD_EXPORT_CONTACT]
604        };
605
606        let event = self.send(&data, Some(EventType::ContactUri)).await?;
607
608        match event.payload {
609            EventPayload::String(uri) => Ok(uri),
610            _ => Err(Error::protocol("Unexpected response to export contact")),
611        }
612    }
613
614    /// Import contact from card data
615    ///
616    /// Format: [CMD_IMPORT_CONTACT=0x12][card_data]
617    pub async fn import_contact(&self, card_data: &[u8]) -> Result<()> {
618        let mut data = vec![CMD_IMPORT_CONTACT];
619        data.extend_from_slice(card_data);
620        self.send(&data, Some(EventType::Ok)).await?;
621        Ok(())
622    }
623
624    // ========== Messaging Commands ==========
625
626    /// Get the next message from the queue
627    ///
628    /// Returns the event containing either a `ContactMessage` or `ChannelMessage` payload.
629    /// Returns `None` if there are no more messages.
630    ///
631    /// The caller should check `event.event_type` to determine the message type:
632    /// - `EventType::ContactMsgRecv` → `EventPayload::ContactMessage(msg)`
633    /// - `EventType::ChannelMsgRecv` → `EventPayload::ChannelMessage(msg)`
634    pub async fn get_msg(&self) -> Result<Option<MeshCoreEvent>> {
635        self.get_msg_with_timeout(self.default_timeout).await
636    }
637
638    /// Get the next message with a custom timeout
639    ///
640    /// Format: [CMD_SYNC_NEXT_MESSAGE=0x0A]
641    pub async fn get_msg_with_timeout(&self, timeout: Duration) -> Result<Option<MeshCoreEvent>> {
642        let data = [CMD_SYNC_NEXT_MESSAGE];
643        let event = self
644            .send_multi(
645                &data,
646                &[
647                    EventType::ContactMsgRecv,
648                    EventType::ChannelMsgRecv,
649                    EventType::NoMoreMessages,
650                    EventType::Error,
651                ],
652                timeout,
653            )
654            .await?;
655
656        match event.event_type {
657            EventType::ContactMsgRecv | EventType::ChannelMsgRecv => Ok(Some(event)),
658            EventType::NoMoreMessages => Ok(None),
659            EventType::Error => match event.payload {
660                EventPayload::String(msg) => Err(Error::device(msg)),
661                _ => Err(Error::device("Unknown error")),
662            },
663            _ => Err(Error::protocol("Unexpected event type")),
664        }
665    }
666
667    /// Send a message to a contact
668    ///
669    /// Format: [CMD_SEND_TXT_MSG=0x02][txt_type][attempt][timestamp: u32][pubkey_prefix: 6][message]
670    pub async fn send_msg(
671        &self,
672        dest: impl Into<Destination>,
673        msg: &str,
674        timestamp: Option<u32>,
675    ) -> Result<MsgSentInfo> {
676        let dest: Destination = dest.into();
677        let prefix = dest.prefix()?;
678        let ts = timestamp.unwrap_or_else(|| {
679            std::time::SystemTime::now()
680                .duration_since(std::time::UNIX_EPOCH)
681                .unwrap()
682                .as_secs() as u32
683        });
684
685        // TXT_TYPE_PLAIN = 0, attempt = 0
686        let mut data = vec![CMD_SEND_TXT_MSG, 0x00, 0x00]; // Second 0x00 is "attempt"
687        data.extend_from_slice(&ts.to_le_bytes());
688        data.extend_from_slice(&prefix);
689        data.extend_from_slice(msg.as_bytes());
690
691        let event = self
692            .send_with_timeout(&data, Some(EventType::MsgSent), Duration::from_secs(10))
693            .await?;
694
695        if event.event_type == EventType::Error {
696            return match event.payload {
697                EventPayload::String(error_message) => Err(Error::protocol(error_message)),
698                _ => Err(Error::protocol("Unexpected response to send_msg")),
699            };
700        }
701
702        match event.payload {
703            EventPayload::MsgSent(info) => Ok(info),
704            _ => Err(Error::protocol("Unexpected response to send_msg")),
705        }
706    }
707
708    /// Send a channel message
709    ///
710    /// Format: [CMD_SEND_CHANNEL_TXT_MSG=0x03][txt_type][channel_idx][timestamp: u32][message]
711    pub async fn send_channel_msg(
712        &self,
713        channel: u8,
714        msg: &str,
715        timestamp: Option<u32>,
716    ) -> Result<()> {
717        let ts = timestamp.unwrap_or_else(|| {
718            std::time::SystemTime::now()
719                .duration_since(std::time::UNIX_EPOCH)
720                .unwrap()
721                .as_secs() as u32
722        });
723
724        // TXT_TYPE_PLAIN = 0
725        let mut data = vec![CMD_SEND_CHANNEL_TXT_MSG, 0x00, channel];
726        data.extend_from_slice(&ts.to_le_bytes());
727        data.extend_from_slice(msg.as_bytes());
728
729        let _ = self.send(&data, Some(EventType::Ok)).await?;
730
731        Ok(())
732    }
733
734    /// Send login request
735    ///
736    /// Format: [CMD_SEND_LOGIN=0x1A][pubkey: 32][password]
737    pub async fn send_login(
738        &self,
739        dest: impl Into<Destination>,
740        password: &str,
741    ) -> Result<MsgSentInfo> {
742        let dest: Destination = dest.into();
743        let pubkey = dest
744            .public_key()
745            .ok_or_else(|| Error::invalid_param("Login requires full 32-byte public key"))?;
746
747        let mut data = vec![CMD_SEND_LOGIN];
748        data.extend_from_slice(&pubkey);
749        data.extend_from_slice(password.as_bytes());
750
751        let event = self.send(&data, Some(EventType::MsgSent)).await?;
752
753        match event.payload {
754            EventPayload::MsgSent(info) => Ok(info),
755            _ => Err(Error::protocol("Unexpected response to send_login")),
756        }
757    }
758
759    /// Send logout request
760    ///
761    /// Format: [CMD_LOGOUT=0x1D][pubkey: 32]
762    pub async fn send_logout(&self, dest: impl Into<Destination>) -> Result<()> {
763        let dest: Destination = dest.into();
764        let pubkey = dest
765            .public_key()
766            .ok_or_else(|| Error::invalid_param("Logout requires full 32-byte public key"))?;
767
768        let mut data = vec![CMD_LOGOUT];
769        data.extend_from_slice(&pubkey);
770
771        self.send(&data, Some(EventType::Ok)).await?;
772        Ok(())
773    }
774
775    // ========== Binary Commands ==========
776
777    /// Send a binary request to a contact
778    ///
779    /// Format: [CMD_SEND_BINARY_REQ=0x32][req_type][pubkey: 32]
780    pub async fn send_binary_req(
781        &self,
782        dest: impl Into<Destination>,
783        req_type: BinaryReqType,
784    ) -> Result<MsgSentInfo> {
785        let dest: Destination = dest.into();
786        let pubkey = dest.public_key().ok_or_else(|| {
787            Error::invalid_param("Binary request requires full 32-byte public key")
788        })?;
789
790        let mut data = vec![CMD_SEND_BINARY_REQ];
791        data.push(req_type as u8);
792        data.extend_from_slice(&pubkey);
793
794        let event = self.send(&data, Some(EventType::MsgSent)).await?;
795
796        match event.payload {
797            EventPayload::MsgSent(info) => {
798                // Register the binary request for response matching
799                self.reader
800                    .register_binary_request(
801                        &info.expected_ack,
802                        req_type,
803                        pubkey.to_vec(),
804                        Duration::from_millis(info.suggested_timeout as u64),
805                        HashMap::new(),
806                        false,
807                    )
808                    .await;
809                Ok(info)
810            }
811            _ => Err(Error::protocol("Unexpected response to binary request")),
812        }
813    }
814
815    /// Request status from a contact
816    pub async fn request_status(&self, dest: impl Into<Destination>) -> Result<StatusData> {
817        self.request_status_with_timeout(dest, self.default_timeout)
818            .await
819    }
820
821    /// Request status with the custom timeout
822    pub async fn request_status_with_timeout(
823        &self,
824        dest: impl Into<Destination>,
825        timeout: Duration,
826    ) -> Result<StatusData> {
827        let sent = self.send_binary_req(dest, BinaryReqType::Status).await?;
828
829        let mut filters = HashMap::new();
830        filters.insert("tag".to_string(), hex_encode(&sent.expected_ack));
831
832        let event = self
833            .wait_for_event(Some(EventType::StatusResponse), filters, timeout)
834            .await?;
835
836        match event.payload {
837            EventPayload::Status(status) => Ok(status),
838            _ => Err(Error::protocol("Unexpected response to status request")),
839        }
840    }
841
842    /// Request telemetry from a contact
843    pub async fn request_telemetry(&self, dest: impl Into<Destination>) -> Result<Vec<u8>> {
844        self.request_telemetry_with_timeout(dest, self.default_timeout)
845            .await
846    }
847
848    /// Request telemetry with the custom timeout
849    pub async fn request_telemetry_with_timeout(
850        &self,
851        dest: impl Into<Destination>,
852        timeout: Duration,
853    ) -> Result<Vec<u8>> {
854        let sent = self.send_binary_req(dest, BinaryReqType::Telemetry).await?;
855
856        let mut filters = HashMap::new();
857        filters.insert("tag".to_string(), hex_encode(&sent.expected_ack));
858
859        let event = self
860            .wait_for_event(Some(EventType::TelemetryResponse), filters, timeout)
861            .await?;
862
863        match event.payload {
864            EventPayload::Telemetry(data) => Ok(data),
865            _ => Err(Error::protocol("Unexpected response to telemetry request")),
866        }
867    }
868
869    /// Request ACL from a contact
870    pub async fn request_acl(&self, dest: impl Into<Destination>) -> Result<Vec<AclEntry>> {
871        self.request_acl_with_timeout(dest, self.default_timeout)
872            .await
873    }
874
875    /// Request ACL with the custom timeout
876    pub async fn request_acl_with_timeout(
877        &self,
878        dest: impl Into<Destination>,
879        timeout: Duration,
880    ) -> Result<Vec<AclEntry>> {
881        let sent = self.send_binary_req(dest, BinaryReqType::Acl).await?;
882
883        let mut filters = HashMap::new();
884        filters.insert("tag".to_string(), hex_encode(&sent.expected_ack));
885
886        let event = self
887            .wait_for_event(Some(EventType::AclResponse), filters, timeout)
888            .await?;
889
890        match event.payload {
891            EventPayload::Acl(entries) => Ok(entries),
892            _ => Err(Error::protocol("Unexpected response to ACL request")),
893        }
894    }
895
896    /// Request neighbours from a contact
897    pub async fn request_neighbours(
898        &self,
899        dest: impl Into<Destination>,
900        count: u16,
901        offset: u16,
902    ) -> Result<NeighboursData> {
903        self.request_neighbours_with_timeout(dest, count, offset, self.default_timeout)
904            .await
905    }
906
907    /// Request neighbours with custom timeout
908    ///
909    /// Format: [CMD_SEND_BINARY_REQ=0x32][req_type][pubkey: 32][count: u16][offset: u16]
910    pub async fn request_neighbours_with_timeout(
911        &self,
912        dest: impl Into<Destination>,
913        count: u16,
914        offset: u16,
915        timeout: Duration,
916    ) -> Result<NeighboursData> {
917        let dest: Destination = dest.into();
918        let pubkey = dest.public_key().ok_or_else(|| {
919            Error::invalid_param("Neighbours request requires full 32-byte public key")
920        })?;
921
922        let mut data = vec![CMD_SEND_BINARY_REQ];
923        data.push(BinaryReqType::Neighbours as u8);
924        data.extend_from_slice(&pubkey);
925        data.extend_from_slice(&count.to_le_bytes());
926        data.extend_from_slice(&offset.to_le_bytes());
927
928        let event = self.send(&data, Some(EventType::MsgSent)).await?;
929        let sent = match event.payload {
930            EventPayload::MsgSent(info) => info,
931            _ => return Err(Error::protocol("Unexpected response to neighbours request")),
932        };
933
934        // Register the request
935        self.reader
936            .register_binary_request(
937                &sent.expected_ack,
938                BinaryReqType::Neighbours,
939                pubkey.to_vec(),
940                timeout,
941                HashMap::new(),
942                false,
943            )
944            .await;
945
946        let mut filters = HashMap::new();
947        filters.insert("tag".to_string(), hex_encode(&sent.expected_ack));
948
949        let event = self
950            .wait_for_event(Some(EventType::NeighboursResponse), filters, timeout)
951            .await?;
952
953        match event.payload {
954            EventPayload::Neighbours(data) => Ok(data),
955            _ => Err(Error::protocol("Unexpected response to neighbours request")),
956        }
957    }
958
959    // ========== Signing Commands ==========
960
961    /// Start a signing session
962    ///
963    /// Format: [CMD_SIGN_START=0x21]
964    pub async fn sign_start(&self) -> Result<u32> {
965        let data = [CMD_SIGN_START];
966        let event = self.send(&data, Some(EventType::SignStart)).await?;
967
968        match event.payload {
969            EventPayload::SignStart { max_length } => Ok(max_length),
970            _ => Err(Error::protocol("Unexpected response to sign_start")),
971        }
972    }
973
974    /// Send data chunk for signing
975    ///
976    /// Format: [CMD_SIGN_DATA=0x22][chunk]
977    pub async fn sign_data(&self, chunk: &[u8]) -> Result<()> {
978        let mut data = vec![CMD_SIGN_DATA];
979        data.extend_from_slice(chunk);
980        self.send(&data, Some(EventType::Ok)).await?;
981        Ok(())
982    }
983
984    /// Finish signing and get the signature
985    ///
986    /// Format: [CMD_SIGN_FINISH=0x23]
987    pub async fn sign_finish(&self, timeout: Duration) -> Result<Vec<u8>> {
988        let data = [CMD_SIGN_FINISH];
989        let event = self
990            .send_with_timeout(&data, Some(EventType::Signature), timeout)
991            .await?;
992
993        match event.payload {
994            EventPayload::Signature(sig) => Ok(sig),
995            _ => Err(Error::protocol("Unexpected response to sign_finish")),
996        }
997    }
998
999    /// Sign data (high-level helper)
1000    pub async fn sign(&self, data_to_sign: &[u8], chunk_size: usize) -> Result<Vec<u8>> {
1001        let max_length = self.sign_start().await?;
1002
1003        if data_to_sign.len() > max_length as usize {
1004            return Err(Error::invalid_param(format!(
1005                "Data too large: {} > {}",
1006                data_to_sign.len(),
1007                max_length
1008            )));
1009        }
1010
1011        // Send data in chunks
1012        for chunk in data_to_sign.chunks(chunk_size) {
1013            self.sign_data(chunk).await?;
1014        }
1015
1016        // Get signature with extended timeout
1017        let timeout = Duration::from_secs(30);
1018        self.sign_finish(timeout).await
1019    }
1020}
1021
1022#[cfg(test)]
1023mod tests {
1024    use super::*;
1025
1026    // ========== Destination Tests ==========
1027
1028    #[test]
1029    fn test_destination_from_bytes_slice() {
1030        let bytes: &[u8] = &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
1031        let dest: Destination = bytes.into();
1032        assert!(matches!(dest, Destination::Bytes(_)));
1033    }
1034
1035    #[test]
1036    fn test_destination_from_vec() {
1037        let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
1038        let dest: Destination = bytes.into();
1039        assert!(matches!(dest, Destination::Bytes(_)));
1040    }
1041
1042    #[test]
1043    fn test_destination_from_str() {
1044        let dest: Destination = "0102030405060708".into();
1045        assert!(matches!(dest, Destination::Hex(_)));
1046    }
1047
1048    #[test]
1049    fn test_destination_from_string() {
1050        let dest: Destination = String::from("0102030405060708").into();
1051        assert!(matches!(dest, Destination::Hex(_)));
1052    }
1053
1054    #[test]
1055    fn test_destination_from_contact() {
1056        let contact = Contact {
1057            public_key: [0xAA; 32],
1058            contact_type: 1,
1059            flags: 0,
1060            path_len: -1,
1061            out_path: vec![],
1062            adv_name: "Test".to_string(),
1063            last_advert: 0,
1064            adv_lat: 0,
1065            adv_lon: 0,
1066            last_modification_timestamp: 0,
1067        };
1068        let dest: Destination = contact.into();
1069        assert!(matches!(dest, Destination::Contact(_)));
1070    }
1071
1072    #[test]
1073    fn test_destination_from_contact_ref() {
1074        let contact = Contact {
1075            public_key: [0xBB; 32],
1076            contact_type: 1,
1077            flags: 0,
1078            path_len: -1,
1079            out_path: vec![],
1080            adv_name: "Test".to_string(),
1081            last_advert: 0,
1082            adv_lat: 0,
1083            adv_lon: 0,
1084            last_modification_timestamp: 0,
1085        };
1086        let dest: Destination = (&contact).into();
1087        assert!(matches!(dest, Destination::Contact(_)));
1088    }
1089
1090    #[test]
1091    fn test_destination_prefix_from_bytes() {
1092        let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
1093        let dest: Destination = bytes.into();
1094        let prefix = dest.prefix().unwrap();
1095        assert_eq!(prefix, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1096    }
1097
1098    #[test]
1099    fn test_destination_prefix_from_bytes_too_short() {
1100        let bytes = vec![0x01, 0x02, 0x03];
1101        let dest: Destination = bytes.into();
1102        assert!(dest.prefix().is_err());
1103    }
1104
1105    #[test]
1106    fn test_destination_prefix_from_hex() {
1107        let dest: Destination = "010203040506".into();
1108        let prefix = dest.prefix().unwrap();
1109        assert_eq!(prefix, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
1110    }
1111
1112    #[test]
1113    fn test_destination_prefix_from_hex_too_short() {
1114        let dest: Destination = "0102".into();
1115        assert!(dest.prefix().is_err());
1116    }
1117
1118    #[test]
1119    fn test_destination_prefix_from_contact() {
1120        let mut public_key = [0u8; 32];
1121        public_key[0..6].copy_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1122        let contact = Contact {
1123            public_key,
1124            contact_type: 1,
1125            flags: 0,
1126            path_len: -1,
1127            out_path: vec![],
1128            adv_name: "Test".to_string(),
1129            last_advert: 0,
1130            adv_lat: 0,
1131            adv_lon: 0,
1132            last_modification_timestamp: 0,
1133        };
1134        let dest: Destination = contact.into();
1135        let prefix = dest.prefix().unwrap();
1136        assert_eq!(prefix, [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
1137    }
1138
1139    #[test]
1140    fn test_destination_public_key_from_bytes_32() {
1141        let bytes = vec![0xAA; 32];
1142        let dest: Destination = bytes.into();
1143        let key = dest.public_key().unwrap();
1144        assert_eq!(key, [0xAA; 32]);
1145    }
1146
1147    #[test]
1148    fn test_destination_public_key_from_bytes_short() {
1149        let bytes = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
1150        let dest: Destination = bytes.into();
1151        assert!(dest.public_key().is_none());
1152    }
1153
1154    #[test]
1155    fn test_destination_public_key_from_hex_32() {
1156        // 32 bytes = 64 hex chars
1157        let hex = "aa".repeat(32);
1158        let dest: Destination = hex.into();
1159        let key = dest.public_key().unwrap();
1160        assert_eq!(key, [0xAA; 32]);
1161    }
1162
1163    #[test]
1164    fn test_destination_public_key_from_hex_short() {
1165        let dest: Destination = "010203040506".into();
1166        assert!(dest.public_key().is_none());
1167    }
1168
1169    #[test]
1170    fn test_destination_public_key_from_contact() {
1171        let contact = Contact {
1172            public_key: [0xCC; 32],
1173            contact_type: 1,
1174            flags: 0,
1175            path_len: -1,
1176            out_path: vec![],
1177            adv_name: "Test".to_string(),
1178            last_advert: 0,
1179            adv_lat: 0,
1180            adv_lon: 0,
1181            last_modification_timestamp: 0,
1182        };
1183        let dest: Destination = contact.into();
1184        let key = dest.public_key().unwrap();
1185        assert_eq!(key, [0xCC; 32]);
1186    }
1187
1188    #[test]
1189    fn test_destination_clone() {
1190        let dest = Destination::Hex("0102030405060708".to_string());
1191        let cloned = dest.clone();
1192        assert!(matches!(cloned, Destination::Hex(_)));
1193    }
1194
1195    #[test]
1196    fn test_destination_debug() {
1197        let dest = Destination::Bytes(vec![1, 2, 3]);
1198        let debug_str = format!("{:?}", dest);
1199        assert!(debug_str.contains("Bytes"));
1200    }
1201
1202    // ========== Constants Tests ==========
1203
1204    #[test]
1205    fn test_default_timeout() {
1206        assert_eq!(DEFAULT_TIMEOUT, Duration::from_secs(5));
1207    }
1208
1209    #[test]
1210    fn test_command_constants() {
1211        assert_eq!(CMD_APP_START, 1);
1212        assert_eq!(CMD_SEND_TXT_MSG, 2);
1213        assert_eq!(CMD_SEND_CHANNEL_TXT_MSG, 3);
1214        assert_eq!(CMD_GET_CONTACTS, 4);
1215        assert_eq!(CMD_GET_DEVICE_TIME, 5);
1216        assert_eq!(CMD_SET_DEVICE_TIME, 6);
1217        assert_eq!(CMD_SEND_SELF_ADVERT, 7);
1218        assert_eq!(CMD_SET_ADVERT_NAME, 8);
1219        assert_eq!(CMD_ADD_UPDATE_CONTACT, 9);
1220        assert_eq!(CMD_SYNC_NEXT_MESSAGE, 10);
1221        assert_eq!(CMD_SET_RADIO_TX_POWER, 12);
1222        assert_eq!(CMD_SET_ADVERT_LATLON, 14);
1223        assert_eq!(CMD_REMOVE_CONTACT, 15);
1224        assert_eq!(CMD_EXPORT_CONTACT, 17);
1225        assert_eq!(CMD_IMPORT_CONTACT, 18);
1226        assert_eq!(CMD_REBOOT, 19);
1227        assert_eq!(CMD_GET_BATT_AND_STORAGE, 20);
1228        assert_eq!(CMD_DEVICE_QUERY, 22);
1229        assert_eq!(CMD_EXPORT_PRIVATE_KEY, 23);
1230        assert_eq!(CMD_IMPORT_PRIVATE_KEY, 24);
1231        assert_eq!(CMD_SEND_LOGIN, 26);
1232        assert_eq!(CMD_LOGOUT, 29);
1233        assert_eq!(CMD_GET_CHANNEL, 31);
1234        assert_eq!(CMD_SET_CHANNEL, 32);
1235        assert_eq!(CMD_SIGN_START, 33);
1236        assert_eq!(CMD_SIGN_DATA, 34);
1237        assert_eq!(CMD_SIGN_FINISH, 35);
1238        assert_eq!(CMD_GET_CUSTOM_VARS, 40);
1239        assert_eq!(CMD_SET_CUSTOM_VAR, 41);
1240        assert_eq!(CMD_SEND_BINARY_REQ, 50);
1241    }
1242
1243    // ========== CommandHandler Tests with Mock Infrastructure ==========
1244
1245    fn create_test_handler() -> (
1246        CommandHandler,
1247        mpsc::Receiver<Vec<u8>>,
1248        Arc<EventDispatcher>,
1249    ) {
1250        let (sender, receiver) = mpsc::channel(16);
1251        let dispatcher = Arc::new(EventDispatcher::new());
1252        let reader = Arc::new(MessageReader::new(dispatcher.clone()));
1253        let handler = CommandHandler::new(sender, dispatcher.clone(), reader);
1254        (handler, receiver, dispatcher)
1255    }
1256
1257    #[tokio::test]
1258    async fn test_command_handler_new() {
1259        let (handler, _rx, _dispatcher) = create_test_handler();
1260        assert_eq!(handler.default_timeout, DEFAULT_TIMEOUT);
1261    }
1262
1263    #[tokio::test]
1264    async fn test_command_handler_set_default_timeout() {
1265        let (mut handler, _rx, _dispatcher) = create_test_handler();
1266        handler.set_default_timeout(Duration::from_secs(10));
1267        assert_eq!(handler.default_timeout, Duration::from_secs(10));
1268    }
1269
1270    #[tokio::test]
1271    async fn test_command_handler_send_timeout() {
1272        let (handler, mut rx, _dispatcher) = create_test_handler();
1273
1274        // Spawn a task to receive the data
1275        let recv_task = tokio::spawn(async move { rx.recv().await });
1276
1277        // Send with a short timeout - should the timeout since no response comes
1278        let result = handler
1279            .send_with_timeout(&[0x01], Some(EventType::Ok), Duration::from_millis(10))
1280            .await;
1281
1282        assert!(result.is_err());
1283
1284        // Verify data was sent
1285        let sent = recv_task.await.unwrap();
1286        assert_eq!(sent, Some(vec![0x01]));
1287    }
1288
1289    #[tokio::test]
1290    async fn test_command_handler_send_with_response() {
1291        let (handler, mut rx, dispatcher) = create_test_handler();
1292
1293        // Spawn a task that sends a response
1294        let dispatcher_clone = dispatcher.clone();
1295        tokio::spawn(async move {
1296            // Wait for the command to be sent
1297            let _sent = rx.recv().await;
1298            // Emit the expected response
1299            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1300        });
1301
1302        let result = handler
1303            .send_with_timeout(&[0x01], Some(EventType::Ok), Duration::from_millis(100))
1304            .await;
1305
1306        assert!(result.is_ok());
1307        assert_eq!(result.unwrap().event_type, EventType::Ok);
1308    }
1309
1310    #[tokio::test]
1311    async fn test_command_handler_wait_for_event_timeout() {
1312        let (handler, _rx, _dispatcher) = create_test_handler();
1313
1314        let result = handler
1315            .wait_for_event(
1316                Some(EventType::Ok),
1317                HashMap::new(),
1318                Duration::from_millis(10),
1319            )
1320            .await;
1321
1322        assert!(result.is_err());
1323    }
1324
1325    #[tokio::test]
1326    async fn test_command_handler_wait_for_event_success() {
1327        let (handler, _rx, dispatcher) = create_test_handler();
1328
1329        // Emit an event
1330        tokio::spawn(async move {
1331            tokio::time::sleep(Duration::from_millis(5)).await;
1332            dispatcher.emit(MeshCoreEvent::ok()).await;
1333        });
1334
1335        let result = handler
1336            .wait_for_event(
1337                Some(EventType::Ok),
1338                HashMap::new(),
1339                Duration::from_millis(100),
1340            )
1341            .await;
1342
1343        assert!(result.is_ok());
1344    }
1345
1346    #[tokio::test]
1347    async fn test_command_handler_wait_for_any_event() {
1348        let (handler, _rx, dispatcher) = create_test_handler();
1349
1350        // Emit an error event
1351        tokio::spawn(async move {
1352            tokio::time::sleep(Duration::from_millis(5)).await;
1353            dispatcher.emit(MeshCoreEvent::error("test")).await;
1354        });
1355
1356        let result = handler
1357            .wait_for_any_event(
1358                &[EventType::Ok, EventType::Error],
1359                Duration::from_millis(100),
1360            )
1361            .await;
1362
1363        assert!(result.is_ok());
1364        assert_eq!(result.unwrap().event_type, EventType::Error);
1365    }
1366
1367    #[tokio::test]
1368    async fn test_command_handler_wait_for_any_event_timeout() {
1369        let (handler, _rx, _dispatcher) = create_test_handler();
1370
1371        let result = handler
1372            .wait_for_any_event(
1373                &[EventType::Ok, EventType::Error],
1374                Duration::from_millis(10),
1375            )
1376            .await;
1377
1378        assert!(result.is_err());
1379    }
1380
1381    #[tokio::test]
1382    async fn test_command_handler_send_multi() {
1383        let (handler, mut rx, dispatcher) = create_test_handler();
1384
1385        // Spawn responder
1386        let dispatcher_clone = dispatcher.clone();
1387        tokio::spawn(async move {
1388            let _sent = rx.recv().await;
1389            dispatcher_clone
1390                .emit(MeshCoreEvent::error("device busy"))
1391                .await;
1392        });
1393
1394        let result = handler
1395            .send_multi(
1396                &[0x01],
1397                &[EventType::Ok, EventType::Error],
1398                Duration::from_millis(100),
1399            )
1400            .await;
1401
1402        assert!(result.is_ok());
1403        assert_eq!(result.unwrap().event_type, EventType::Error);
1404    }
1405
1406    #[tokio::test]
1407    async fn test_send_appstart_success() {
1408        let (handler, mut rx, dispatcher) = create_test_handler();
1409
1410        let dispatcher_clone = dispatcher.clone();
1411        tokio::spawn(async move {
1412            let sent = rx.recv().await.unwrap();
1413            // Verify APPSTART command format
1414            assert_eq!(sent[0], CMD_APP_START);
1415            assert_eq!(&sent[8..13], b"mccli");
1416
1417            // Send SelfInfo response
1418            let info = SelfInfo {
1419                adv_type: 1,
1420                tx_power: 20,
1421                max_tx_power: 30,
1422                public_key: [0; 32],
1423                adv_lat: 0,
1424                adv_lon: 0,
1425                multi_acks: 0,
1426                adv_loc_policy: 0,
1427                telemetry_mode_base: 0,
1428                telemetry_mode_loc: 0,
1429                telemetry_mode_env: 0,
1430                manual_add_contacts: false,
1431                radio_freq: 915000000,
1432                radio_bw: 125000,
1433                sf: 7,
1434                cr: 5,
1435                name: "TestDevice".to_string(),
1436            };
1437            dispatcher_clone
1438                .emit(MeshCoreEvent::new(
1439                    EventType::SelfInfo,
1440                    EventPayload::SelfInfo(info),
1441                ))
1442                .await;
1443        });
1444
1445        let result = handler.send_appstart().await;
1446        assert!(result.is_ok());
1447        let info = result.unwrap();
1448        assert_eq!(info.name, "TestDevice");
1449        assert_eq!(info.tx_power, 20);
1450    }
1451
1452    #[tokio::test]
1453    async fn test_get_bat_success() {
1454        let (handler, mut rx, dispatcher) = create_test_handler();
1455
1456        let dispatcher_clone = dispatcher.clone();
1457        tokio::spawn(async move {
1458            let sent = rx.recv().await.unwrap();
1459            assert_eq!(sent[0], CMD_GET_BATT_AND_STORAGE);
1460
1461            let info = BatteryInfo {
1462                battery_mv: 4200,
1463                used_kb: Some(512),
1464                total_kb: Some(4096),
1465            };
1466            dispatcher_clone
1467                .emit(MeshCoreEvent::new(
1468                    EventType::Battery,
1469                    EventPayload::Battery(info),
1470                ))
1471                .await;
1472        });
1473
1474        let result = handler.get_bat().await;
1475        assert!(result.is_ok());
1476        let info = result.unwrap();
1477        assert_eq!(info.battery_mv, 4200);
1478        assert!((info.voltage() - 4.2).abs() < 0.001);
1479    }
1480
1481    #[tokio::test]
1482    async fn test_get_time_success() {
1483        let (handler, mut rx, dispatcher) = create_test_handler();
1484
1485        let dispatcher_clone = dispatcher.clone();
1486        tokio::spawn(async move {
1487            let sent = rx.recv().await.unwrap();
1488            assert_eq!(sent[0], CMD_GET_DEVICE_TIME);
1489
1490            dispatcher_clone
1491                .emit(MeshCoreEvent::new(
1492                    EventType::CurrentTime,
1493                    EventPayload::Time(1234567890),
1494                ))
1495                .await;
1496        });
1497
1498        let result = handler.get_time().await;
1499        assert!(result.is_ok());
1500        assert_eq!(result.unwrap(), 1234567890);
1501    }
1502
1503    #[tokio::test]
1504    async fn test_set_time_success() {
1505        let (handler, mut rx, dispatcher) = create_test_handler();
1506
1507        let dispatcher_clone = dispatcher.clone();
1508        tokio::spawn(async move {
1509            let sent = rx.recv().await.unwrap();
1510            assert_eq!(sent[0], CMD_SET_DEVICE_TIME);
1511            // Verify the timestamp is included
1512            let ts = u32::from_le_bytes([sent[1], sent[2], sent[3], sent[4]]);
1513            assert_eq!(ts, 1234567890);
1514
1515            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1516        });
1517
1518        let result = handler.set_time(1234567890).await;
1519        assert!(result.is_ok());
1520    }
1521
1522    #[tokio::test]
1523    async fn test_set_name_success() {
1524        let (handler, mut rx, dispatcher) = create_test_handler();
1525
1526        let dispatcher_clone = dispatcher.clone();
1527        tokio::spawn(async move {
1528            let sent = rx.recv().await.unwrap();
1529            assert_eq!(sent[0], CMD_SET_ADVERT_NAME);
1530            assert_eq!(&sent[1..], b"MyNode");
1531
1532            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1533        });
1534
1535        let result = handler.set_name("MyNode").await;
1536        assert!(result.is_ok());
1537    }
1538
1539    #[tokio::test]
1540    async fn test_set_coords_success() {
1541        let (handler, mut rx, dispatcher) = create_test_handler();
1542
1543        let dispatcher_clone = dispatcher.clone();
1544        tokio::spawn(async move {
1545            let sent = rx.recv().await.unwrap();
1546            assert_eq!(sent[0], CMD_SET_ADVERT_LATLON);
1547            // Verify coordinates are present (lat + lon = 8 bytes after command)
1548            assert!(sent.len() >= 9);
1549
1550            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1551        });
1552
1553        let result = handler.set_coords(37.7749, -122.4194).await;
1554        assert!(result.is_ok());
1555    }
1556
1557    #[tokio::test]
1558    async fn test_set_tx_power_success() {
1559        let (handler, mut rx, dispatcher) = create_test_handler();
1560
1561        let dispatcher_clone = dispatcher.clone();
1562        tokio::spawn(async move {
1563            let sent = rx.recv().await.unwrap();
1564            assert_eq!(sent[0], CMD_SET_RADIO_TX_POWER);
1565            assert_eq!(sent[1], 20);
1566
1567            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1568        });
1569
1570        let result = handler.set_tx_power(20).await;
1571        assert!(result.is_ok());
1572    }
1573
1574    #[tokio::test]
1575    async fn test_send_advert_flood() {
1576        let (handler, mut rx, dispatcher) = create_test_handler();
1577
1578        let dispatcher_clone = dispatcher.clone();
1579        tokio::spawn(async move {
1580            let sent = rx.recv().await.unwrap();
1581            assert_eq!(sent[0], CMD_SEND_SELF_ADVERT);
1582            assert_eq!(sent[1], 0x01); // flood flag
1583
1584            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1585        });
1586
1587        let result = handler.send_advert(true).await;
1588        assert!(result.is_ok());
1589    }
1590
1591    #[tokio::test]
1592    async fn test_send_advert_no_flood() {
1593        let (handler, mut rx, dispatcher) = create_test_handler();
1594
1595        let dispatcher_clone = dispatcher.clone();
1596        tokio::spawn(async move {
1597            let sent = rx.recv().await.unwrap();
1598            assert_eq!(sent[0], CMD_SEND_SELF_ADVERT);
1599            assert_eq!(sent.len(), 1); // no flood flag
1600
1601            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1602        });
1603
1604        let result = handler.send_advert(false).await;
1605        assert!(result.is_ok());
1606    }
1607
1608    #[tokio::test]
1609    async fn test_reboot() {
1610        let (handler, mut rx, _dispatcher) = create_test_handler();
1611
1612        let recv_task = tokio::spawn(async move { rx.recv().await });
1613
1614        let result = handler.reboot().await;
1615        assert!(result.is_ok());
1616
1617        let sent = recv_task.await.unwrap().unwrap();
1618        assert_eq!(sent[0], CMD_REBOOT);
1619        assert_eq!(&sent[1..], b"reboot");
1620    }
1621
1622    #[tokio::test]
1623    async fn test_get_contacts_success() {
1624        let (handler, mut rx, dispatcher) = create_test_handler();
1625
1626        let dispatcher_clone = dispatcher.clone();
1627        tokio::spawn(async move {
1628            let sent = rx.recv().await.unwrap();
1629            assert_eq!(sent[0], CMD_GET_CONTACTS);
1630
1631            let contacts = vec![Contact {
1632                public_key: [0xAA; 32],
1633                contact_type: 1,
1634                flags: 0,
1635                path_len: 2,
1636                out_path: vec![],
1637                adv_name: "Contact1".to_string(),
1638                last_advert: 0,
1639                adv_lat: 0,
1640                adv_lon: 0,
1641                last_modification_timestamp: 0,
1642            }];
1643            dispatcher_clone
1644                .emit(MeshCoreEvent::new(
1645                    EventType::Contacts,
1646                    EventPayload::Contacts(contacts),
1647                ))
1648                .await;
1649        });
1650
1651        let result = handler.get_contacts(0).await;
1652        assert!(result.is_ok());
1653        let contacts = result.unwrap();
1654        assert_eq!(contacts.len(), 1);
1655        assert_eq!(contacts[0].adv_name, "Contact1");
1656    }
1657
1658    #[tokio::test]
1659    async fn test_export_contact_self() {
1660        let (handler, mut rx, dispatcher) = create_test_handler();
1661
1662        let dispatcher_clone = dispatcher.clone();
1663        tokio::spawn(async move {
1664            let sent = rx.recv().await.unwrap();
1665            assert_eq!(sent[0], CMD_EXPORT_CONTACT);
1666            assert_eq!(sent.len(), 1); // no pubkey for self
1667
1668            dispatcher_clone
1669                .emit(MeshCoreEvent::new(
1670                    EventType::ContactUri,
1671                    EventPayload::String("mod.rs://...".to_string()),
1672                ))
1673                .await;
1674        });
1675
1676        let result: Result<String> = handler.export_contact(None::<&str>).await;
1677        assert!(result.is_ok());
1678        assert!(result.unwrap().starts_with("mod.rs://"));
1679    }
1680
1681    #[tokio::test]
1682    async fn test_get_channel_success() {
1683        let (handler, mut rx, dispatcher) = create_test_handler();
1684
1685        let dispatcher_clone = dispatcher.clone();
1686        tokio::spawn(async move {
1687            let sent = rx.recv().await.unwrap();
1688            assert_eq!(sent[0], CMD_GET_CHANNEL);
1689            assert_eq!(sent[1], 0); // channel idx
1690
1691            let info = ChannelInfoData {
1692                channel_idx: 0,
1693                name: "General".to_string(),
1694                secret: [0; 16],
1695            };
1696            dispatcher_clone
1697                .emit(MeshCoreEvent::new(
1698                    EventType::ChannelInfo,
1699                    EventPayload::ChannelInfo(info),
1700                ))
1701                .await;
1702        });
1703
1704        let result = handler.get_channel(0).await;
1705        assert!(result.is_ok());
1706        assert_eq!(result.unwrap().name, "General");
1707    }
1708
1709    #[tokio::test]
1710    async fn test_set_channel_success() {
1711        let (handler, mut rx, dispatcher) = create_test_handler();
1712
1713        let dispatcher_clone = dispatcher.clone();
1714        tokio::spawn(async move {
1715            let sent = rx.recv().await.unwrap();
1716            assert_eq!(sent[0], CMD_SET_CHANNEL);
1717            assert_eq!(sent[1], 1); // channel_idx
1718                                    // Verify name is padded to CHANNEL_NAME_LEN bytes
1719            assert_eq!(sent.len(), 1 + 1 + CHANNEL_NAME_LEN + CHANNEL_SECRET_LEN);
1720            // Check name starts with "Test"
1721            assert_eq!(&sent[2..6], b"Test");
1722            // Check rest of name is zero-padded
1723            assert!(sent[6..2 + CHANNEL_NAME_LEN].iter().all(|&b| b == 0));
1724            // Check secret
1725            assert_eq!(&sent[2 + CHANNEL_NAME_LEN..], &[0xAA; CHANNEL_SECRET_LEN]);
1726
1727            dispatcher_clone
1728                .emit(MeshCoreEvent::new(EventType::Ok, EventPayload::None))
1729                .await;
1730        });
1731
1732        let secret = [0xAA; CHANNEL_SECRET_LEN];
1733        let result = handler.set_channel(1, "Test", &secret).await;
1734        assert!(result.is_ok());
1735    }
1736
1737    #[tokio::test]
1738    async fn test_set_channel_name_truncation_with_null_terminator() {
1739        let (handler, mut rx, dispatcher) = create_test_handler();
1740
1741        let dispatcher_clone = dispatcher.clone();
1742        tokio::spawn(async move {
1743            let sent = rx.recv().await.unwrap();
1744            assert_eq!(sent[0], CMD_SET_CHANNEL);
1745            // Verify total length is correct
1746            assert_eq!(sent.len(), 1 + 1 + CHANNEL_NAME_LEN + CHANNEL_SECRET_LEN);
1747            // Name should be truncated to CHANNEL_NAME_LEN - 1 bytes to leave room for null
1748            let expected_name = b"This is a very long channel nam"; // 31 bytes
1749            assert_eq!(
1750                &sent[2..2 + CHANNEL_NAME_LEN - 1],
1751                &expected_name[..CHANNEL_NAME_LEN - 1]
1752            );
1753            // Last byte of name field must be null terminator
1754            assert_eq!(
1755                sent[2 + CHANNEL_NAME_LEN - 1],
1756                0,
1757                "Last byte of name field must be null terminator"
1758            );
1759
1760            dispatcher_clone
1761                .emit(MeshCoreEvent::new(EventType::Ok, EventPayload::None))
1762                .await;
1763        });
1764
1765        let secret = [0xBB; CHANNEL_SECRET_LEN];
1766        // Name longer than CHANNEL_NAME_LEN - 1 should be truncated to ensure null termination
1767        let long_name = "This is a very long channel name that exceeds the limit";
1768        let result = handler.set_channel(2, long_name, &secret).await;
1769        assert!(result.is_ok());
1770    }
1771
1772    #[tokio::test]
1773    async fn test_sign_start_success() {
1774        let (handler, mut rx, dispatcher) = create_test_handler();
1775
1776        let dispatcher_clone = dispatcher.clone();
1777        tokio::spawn(async move {
1778            let sent = rx.recv().await.unwrap();
1779            assert_eq!(sent[0], CMD_SIGN_START);
1780
1781            dispatcher_clone
1782                .emit(MeshCoreEvent::new(
1783                    EventType::SignStart,
1784                    EventPayload::SignStart { max_length: 4096 },
1785                ))
1786                .await;
1787        });
1788
1789        let result = handler.sign_start().await;
1790        assert!(result.is_ok());
1791        assert_eq!(result.unwrap(), 4096);
1792    }
1793
1794    #[tokio::test]
1795    async fn test_get_custom_vars_success() {
1796        let (handler, mut rx, dispatcher) = create_test_handler();
1797
1798        let dispatcher_clone = dispatcher.clone();
1799        tokio::spawn(async move {
1800            let sent = rx.recv().await.unwrap();
1801            assert_eq!(sent[0], CMD_GET_CUSTOM_VARS);
1802
1803            let mut vars = HashMap::new();
1804            vars.insert("key1".to_string(), "value1".to_string());
1805            dispatcher_clone
1806                .emit(MeshCoreEvent::new(
1807                    EventType::CustomVars,
1808                    EventPayload::CustomVars(vars),
1809                ))
1810                .await;
1811        });
1812
1813        let result = handler.get_custom_vars().await;
1814        assert!(result.is_ok());
1815        let vars = result.unwrap();
1816        assert_eq!(vars.get("key1"), Some(&"value1".to_string()));
1817    }
1818
1819    #[tokio::test]
1820    async fn test_set_custom_var_success() {
1821        let (handler, mut rx, dispatcher) = create_test_handler();
1822
1823        let dispatcher_clone = dispatcher.clone();
1824        tokio::spawn(async move {
1825            let sent = rx.recv().await.unwrap();
1826            assert_eq!(sent[0], CMD_SET_CUSTOM_VAR);
1827            // Should contain "key=value"
1828            let payload = String::from_utf8_lossy(&sent[1..]);
1829            assert!(payload.contains("mykey=myvalue"));
1830
1831            dispatcher_clone.emit(MeshCoreEvent::ok()).await;
1832        });
1833
1834        let result = handler.set_custom_var("mykey", "myvalue").await;
1835        assert!(result.is_ok());
1836    }
1837
1838    #[tokio::test]
1839    async fn test_get_msg_no_more() {
1840        let (handler, mut rx, dispatcher) = create_test_handler();
1841
1842        let dispatcher_clone = dispatcher.clone();
1843        tokio::spawn(async move {
1844            let sent = rx.recv().await.unwrap();
1845            assert_eq!(sent[0], CMD_SYNC_NEXT_MESSAGE);
1846
1847            dispatcher_clone
1848                .emit(MeshCoreEvent::new(
1849                    EventType::NoMoreMessages,
1850                    EventPayload::None,
1851                ))
1852                .await;
1853        });
1854
1855        let result = handler.get_msg().await;
1856        assert!(result.is_ok());
1857        assert!(result.unwrap().is_none());
1858    }
1859
1860    #[tokio::test]
1861    async fn test_get_msg_with_message() {
1862        let (handler, mut rx, dispatcher) = create_test_handler();
1863
1864        let dispatcher_clone = dispatcher.clone();
1865        tokio::spawn(async move {
1866            let _sent = rx.recv().await.unwrap();
1867
1868            let msg = ContactMessage {
1869                sender_prefix: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
1870                path_len: 2,
1871                txt_type: 1,
1872                sender_timestamp: 1234567890,
1873                text: "Hello!".to_string(),
1874                snr: None,
1875                signature: None,
1876            };
1877            dispatcher_clone
1878                .emit(MeshCoreEvent::new(
1879                    EventType::ContactMsgRecv,
1880                    EventPayload::ContactMessage(msg),
1881                ))
1882                .await;
1883        });
1884
1885        let result = handler.get_msg().await;
1886        assert!(result.is_ok());
1887        let event = result.unwrap().unwrap();
1888        assert_eq!(event.event_type, EventType::ContactMsgRecv);
1889        match event.payload {
1890            EventPayload::ContactMessage(msg) => {
1891                assert_eq!(msg.text, "Hello!");
1892            }
1893            _ => panic!("Expected ContactMessage payload"),
1894        }
1895    }
1896}