Skip to content

Commit 4422284

Browse files
committed
Add list devices option
1 parent 98b6f0b commit 4422284

File tree

3 files changed

+116
-11
lines changed

3 files changed

+116
-11
lines changed

src/ble.rs

+44
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,50 @@ pub struct Device {
2626
}
2727

2828
impl Device {
29+
/// Return a list of all BLE devies as a string representation.
30+
pub async fn list_all() -> Result<Vec<String>> {
31+
// Run device scan
32+
let manager = Manager::new().await.context("create BLE manager")?;
33+
let adapters = manager
34+
.adapters()
35+
.await
36+
.context("enumerate bluetooth adapters")?;
37+
let adapter = adapters.first().context("no bluetooth adapter found")?;
38+
39+
adapter
40+
.start_scan(ScanFilter {
41+
// don't filter by service
42+
services: Vec::new(),
43+
})
44+
.await
45+
.context("bluetooth scan start")?;
46+
time::sleep(Duration::from_secs(2)).await;
47+
48+
let mut devices = Vec::new();
49+
for peripheral in adapter
50+
.peripherals()
51+
.await
52+
.context("enumerating bluetooth devices")?
53+
{
54+
let device = async {
55+
let props = peripheral
56+
.properties()
57+
.await?
58+
.context("missing device info")?;
59+
60+
Ok(format!(
61+
"{}: name={:?} services={:?}",
62+
props.address, props.local_name, props.services
63+
))
64+
};
65+
devices.push(device.await.unwrap_or_else(|err: anyhow::Error| {
66+
format!("{} failed to collect info: {err:?}", peripheral.address())
67+
}));
68+
}
69+
70+
Ok(devices)
71+
}
72+
2973
/// Return all supported devices that are found in two seconds.
3074
///
3175
/// Returns all badges that are in BLE range and are in Bluetooth transfer mode.

src/main.rs

+53-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use badgemagic::{
99
usb_hid::Device as UsbDevice,
1010
};
1111
use base64::Engine;
12-
use clap::Parser;
12+
use clap::{Parser, ValueEnum};
1313
use embedded_graphics::{
1414
geometry::Point,
1515
image::{Image, ImageRawLE},
@@ -43,11 +43,16 @@ struct Args {
4343
#[clap(long)]
4444
transport: TransportProtocol,
4545

46+
/// List all devices visible to a transport and exit
47+
#[clap(long)]
48+
list_devices: bool,
49+
4650
/// Path to TOML configuration file
47-
config: PathBuf,
51+
#[clap(required_unless_present = "list_devices")]
52+
config: Option<PathBuf>,
4853
}
4954

50-
#[derive(Clone, Deserialize, clap::ValueEnum)]
55+
#[derive(Clone, Deserialize, ValueEnum)]
5156
#[serde(rename_all = "kebab-case")]
5257
enum TransportProtocol {
5358
Usb,
@@ -91,16 +96,48 @@ enum Content {
9196
}
9297

9398
fn main() -> Result<()> {
94-
let args = Args::parse();
95-
let config = fs::read_to_string(&args.config)
96-
.with_context(|| format!("load config: {:?}", args.config))?;
99+
let mut args = Args::parse();
100+
101+
if args.list_devices {
102+
return list_devices(&args.transport);
103+
}
104+
105+
let payload = gnerate_payload(&mut args)?;
106+
107+
write_payload(&args.transport, payload)
108+
}
109+
110+
fn list_devices(transport: &TransportProtocol) -> Result<()> {
111+
let devices = match transport {
112+
TransportProtocol::Usb => UsbDevice::list_all(),
113+
TransportProtocol::Ble => tokio::runtime::Builder::new_current_thread()
114+
.enable_all()
115+
.build()?
116+
.block_on(async { BleDevice::list_all().await }),
117+
}?;
97118

119+
eprintln!(
120+
"found {} {} devices",
121+
devices.len(),
122+
transport.to_possible_value().unwrap().get_name(),
123+
);
124+
for device in devices {
125+
println!("- {device}");
126+
}
127+
128+
Ok(())
129+
}
130+
131+
fn gnerate_payload(args: &mut Args) -> Result<PayloadBuffer> {
132+
let config_path = args.config.take().unwrap_or_default();
133+
let config = fs::read_to_string(&config_path)
134+
.with_context(|| format!("load config: {config_path:?}"))?;
98135
let config: Config = {
99136
let extension = args
100137
.format
101138
.as_deref()
102139
.map(AsRef::as_ref)
103-
.or(args.config.extension())
140+
.or(config_path.extension())
104141
.context("missing file extension for config file")?;
105142
match extension.to_str().unwrap_or_default() {
106143
"json" => serde_json::from_str(&config).context("parse config")?,
@@ -189,13 +226,18 @@ fn main() -> Result<()> {
189226
}
190227
}
191228

192-
match args.transport {
229+
Ok(payload)
230+
}
231+
232+
fn write_payload(
233+
transport: &TransportProtocol,
234+
payload: PayloadBuffer,
235+
) -> Result<(), anyhow::Error> {
236+
match transport {
193237
TransportProtocol::Usb => UsbDevice::single()?.write(payload),
194238
TransportProtocol::Ble => tokio::runtime::Builder::new_current_thread()
195239
.enable_all()
196240
.build()?
197241
.block_on(async { BleDevice::single().await?.write(payload).await }),
198-
}?;
199-
200-
Ok(())
242+
}
201243
}

src/usb_hid.rs

+19
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ pub struct Device {
2929
}
3030

3131
impl Device {
32+
/// Return a list of all usb devies as a string representation
33+
pub fn list_all() -> Result<Vec<String>> {
34+
let api = HidApi::new().context("create hid api")?;
35+
let devices = api.device_list();
36+
37+
Ok(devices
38+
.map(|info| {
39+
format!(
40+
"{:?}: vendor_id={:#06x} product_id={:#06x} manufacturer={:?} product={:?}",
41+
info.path(),
42+
info.vendor_id(),
43+
info.product_id(),
44+
info.manufacturer_string(),
45+
info.product_string(),
46+
)
47+
})
48+
.collect())
49+
}
50+
3251
/// Return all supported devices
3352
pub fn enumerate() -> Result<Vec<Self>> {
3453
let api = HidApi::new().context("create hid api")?;

0 commit comments

Comments
 (0)