awear 0.2.0

Rust client for AWEAR EEG devices over BLE using btleplug
Documentation
//! Data types for AWEAR device events and state.

use std::fmt;

/// Device connection status.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeviceStatus {
    Disconnected,
    Connecting,
    Connected,
    Ready,
    Streaming,
    Reconnecting,
    Dfu,
}

impl fmt::Display for DeviceStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Disconnected => write!(f, "disconnected"),
            Self::Connecting => write!(f, "connecting"),
            Self::Connected => write!(f, "connected"),
            Self::Ready => write!(f, "ready"),
            Self::Streaming => write!(f, "streaming"),
            Self::Reconnecting => write!(f, "reconnecting"),
            Self::Dfu => write!(f, "dfu"),
        }
    }
}

/// LUCA data packet types.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum DataPacketType {
    Eeg = 0x01,
    Battery = 0x02,
    Signal = 0x03,
    Misc = 0x04,
}

impl DataPacketType {
    pub fn from_byte(b: u8) -> Option<Self> {
        match b {
            0x01 => Some(Self::Eeg),
            0x02 => Some(Self::Battery),
            0x03 => Some(Self::Signal),
            0x04 => Some(Self::Misc),
            _ => None,
        }
    }
}

/// Events emitted by the AWEAR client.
#[derive(Debug, Clone)]
pub enum AwearEvent {
    /// EEG sample data (single channel, 16-bit signed values).
    Eeg(EegReading),
    /// Battery level (0–100%).
    Battery(u8),
    /// Signal strength (RSSI in dBm).
    Signal(i8),
    /// Misc/command response text.
    Misc(String),
    /// Device connected.
    Connected(String),
    /// Device ready (authenticated).
    Ready,
    /// Device disconnected.
    Disconnected,
    /// Status text update.
    Status(String),
}

/// A block of EEG samples.
#[derive(Debug, Clone)]
pub struct EegReading {
    /// Block sequence counter.
    pub sequence: u32,
    /// 16-bit signed samples (single channel, typically 256 per block).
    pub samples: Vec<i16>,
}

/// Information about a discovered AWEAR device.
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct DiscoveredDevice {
    pub name: String,
    pub id: String,
    pub rssi: i16,
    pub(crate) peripheral: btleplug::platform::Peripheral,
    pub(crate) adapter: btleplug::platform::Adapter,
}

/// Device metadata.
#[derive(Debug, Clone, Default)]
pub struct DeviceInfo {
    pub name: String,
    pub peripheral_uuid: String,
    pub firmware: String,
    pub bootloader: String,
    pub battery: i32,
    pub signal: i32,
    pub is_awear: bool,
}

/// Packet statistics.
#[derive(Debug, Clone, Default)]
pub struct PacketStats {
    pub eeg_packet_count: u64,
    pub battery_packet_count: u64,
    pub signal_packet_count: u64,
    pub misc_packet_count: u64,
    pub lost_packets: u64,
    pub prev_lost_packets: u64,
    pub packet_counter: u8,
    pub prev_packet_counter: u8,
    pub frequency_counter: u64,
    pub last_frequency: f64,
}

/// Client configuration.
#[derive(Debug, Clone)]
pub struct AwearClientConfig {
    pub scan_timeout_secs: u64,
    pub connect_timeout_secs: u64,
    pub reconnect_automatically: bool,
    pub reconnect_timeout_secs: u64,
    pub min_rssi: i16,
    pub name_prefix: String,
    pub auto_start: bool,
}

impl Default for AwearClientConfig {
    fn default() -> Self {
        Self {
            scan_timeout_secs: 10,
            connect_timeout_secs: 30,
            reconnect_automatically: true,
            reconnect_timeout_secs: 10,
            min_rssi: -80,
            name_prefix: "AWEAR".to_string(),
            auto_start: false,
        }
    }
}