Skip to content

Commit

Permalink
Add list devices option
Browse files Browse the repository at this point in the history
  • Loading branch information
mgjm committed Dec 31, 2024
1 parent 98b6f0b commit 4422284
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 11 deletions.
44 changes: 44 additions & 0 deletions src/ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,50 @@ pub struct Device {
}

impl Device {
/// Return a list of all BLE devies as a string representation.
pub async fn list_all() -> Result<Vec<String>> {
// Run device scan
let manager = Manager::new().await.context("create BLE manager")?;
let adapters = manager
.adapters()
.await
.context("enumerate bluetooth adapters")?;
let adapter = adapters.first().context("no bluetooth adapter found")?;

adapter
.start_scan(ScanFilter {
// don't filter by service
services: Vec::new(),
})
.await
.context("bluetooth scan start")?;
time::sleep(Duration::from_secs(2)).await;

let mut devices = Vec::new();
for peripheral in adapter
.peripherals()
.await
.context("enumerating bluetooth devices")?
{
let device = async {
let props = peripheral
.properties()
.await?
.context("missing device info")?;

Ok(format!(
"{}: name={:?} services={:?}",
props.address, props.local_name, props.services
))
};
devices.push(device.await.unwrap_or_else(|err: anyhow::Error| {
format!("{} failed to collect info: {err:?}", peripheral.address())
}));
}

Ok(devices)
}

/// Return all supported devices that are found in two seconds.
///
/// Returns all badges that are in BLE range and are in Bluetooth transfer mode.
Expand Down
64 changes: 53 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use badgemagic::{
usb_hid::Device as UsbDevice,
};
use base64::Engine;
use clap::Parser;
use clap::{Parser, ValueEnum};
use embedded_graphics::{
geometry::Point,
image::{Image, ImageRawLE},
Expand Down Expand Up @@ -43,11 +43,16 @@ struct Args {
#[clap(long)]
transport: TransportProtocol,

/// List all devices visible to a transport and exit
#[clap(long)]
list_devices: bool,

/// Path to TOML configuration file
config: PathBuf,
#[clap(required_unless_present = "list_devices")]
config: Option<PathBuf>,
}

#[derive(Clone, Deserialize, clap::ValueEnum)]
#[derive(Clone, Deserialize, ValueEnum)]
#[serde(rename_all = "kebab-case")]
enum TransportProtocol {
Usb,
Expand Down Expand Up @@ -91,16 +96,48 @@ enum Content {
}

fn main() -> Result<()> {
let args = Args::parse();
let config = fs::read_to_string(&args.config)
.with_context(|| format!("load config: {:?}", args.config))?;
let mut args = Args::parse();

if args.list_devices {
return list_devices(&args.transport);
}

let payload = gnerate_payload(&mut args)?;

write_payload(&args.transport, payload)
}

fn list_devices(transport: &TransportProtocol) -> Result<()> {
let devices = match transport {
TransportProtocol::Usb => UsbDevice::list_all(),
TransportProtocol::Ble => tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async { BleDevice::list_all().await }),
}?;

eprintln!(
"found {} {} devices",
devices.len(),
transport.to_possible_value().unwrap().get_name(),
);
for device in devices {
println!("- {device}");
}

Ok(())
}

fn gnerate_payload(args: &mut Args) -> Result<PayloadBuffer> {
let config_path = args.config.take().unwrap_or_default();
let config = fs::read_to_string(&config_path)
.with_context(|| format!("load config: {config_path:?}"))?;
let config: Config = {
let extension = args
.format
.as_deref()
.map(AsRef::as_ref)
.or(args.config.extension())
.or(config_path.extension())
.context("missing file extension for config file")?;
match extension.to_str().unwrap_or_default() {
"json" => serde_json::from_str(&config).context("parse config")?,
Expand Down Expand Up @@ -189,13 +226,18 @@ fn main() -> Result<()> {
}
}

match args.transport {
Ok(payload)
}

fn write_payload(
transport: &TransportProtocol,
payload: PayloadBuffer,
) -> Result<(), anyhow::Error> {
match transport {
TransportProtocol::Usb => UsbDevice::single()?.write(payload),
TransportProtocol::Ble => tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async { BleDevice::single().await?.write(payload).await }),
}?;

Ok(())
}
}
19 changes: 19 additions & 0 deletions src/usb_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ pub struct Device {
}

impl Device {
/// Return a list of all usb devies as a string representation
pub fn list_all() -> Result<Vec<String>> {
let api = HidApi::new().context("create hid api")?;
let devices = api.device_list();

Ok(devices
.map(|info| {
format!(
"{:?}: vendor_id={:#06x} product_id={:#06x} manufacturer={:?} product={:?}",
info.path(),
info.vendor_id(),
info.product_id(),
info.manufacturer_string(),
info.product_string(),
)
})
.collect())
}

/// Return all supported devices
pub fn enumerate() -> Result<Vec<Self>> {
let api = HidApi::new().context("create hid api")?;
Expand Down

0 comments on commit 4422284

Please sign in to comment.