1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use anyhow::anyhow;
16 use bumble::{
17     adv::{AdvertisementDataBuilder, CommonDataType},
18     wrapper::{
19         device::Device,
20         logging::{bumble_env_logging_level, py_logging_basic_config},
21         transport::Transport,
22     },
23 };
24 use clap::Parser as _;
25 use pyo3::PyResult;
26 use rand::Rng;
27 use std::path;
28 
29 #[pyo3_asyncio::tokio::main]
main() -> PyResult<()>30 async fn main() -> PyResult<()> {
31     env_logger::builder()
32         .filter_level(log::LevelFilter::Info)
33         .init();
34 
35     let cli = Cli::parse();
36 
37     if cli.log_hci {
38         py_logging_basic_config(bumble_env_logging_level("DEBUG"))?;
39     }
40 
41     let transport = Transport::open(cli.transport).await?;
42 
43     let mut device = Device::from_config_file_with_hci(
44         &cli.device_config,
45         transport.source()?,
46         transport.sink()?,
47     )?;
48 
49     let mut adv_data = AdvertisementDataBuilder::new();
50 
51     adv_data
52         .append(
53             CommonDataType::CompleteLocalName,
54             "Bumble from Rust".as_bytes(),
55         )
56         .map_err(|e| anyhow!(e))?;
57 
58     // Randomized TX power
59     adv_data
60         .append(
61             CommonDataType::TxPowerLevel,
62             &[rand::thread_rng().gen_range(-100_i8..=20) as u8],
63         )
64         .map_err(|e| anyhow!(e))?;
65 
66     device.power_on().await?;
67 
68     if cli.extended {
69         println!("Starting extended advertisement...");
70         device.start_advertising_extended(adv_data).await?;
71     } else {
72         device.set_advertising_data(adv_data)?;
73 
74         println!("Starting legacy advertisement...");
75         device.start_advertising(true).await?;
76     }
77 
78     // wait until user kills the process
79     tokio::signal::ctrl_c().await?;
80 
81     if cli.extended {
82         println!("Stopping extended advertisement...");
83         device.stop_advertising_extended().await?;
84     } else {
85         println!("Stopping legacy advertisement...");
86         device.stop_advertising().await?;
87     }
88 
89     Ok(())
90 }
91 
92 #[derive(clap::Parser)]
93 #[command(author, version, about, long_about = None)]
94 struct Cli {
95     /// Bumble device config.
96     ///
97     /// See, for instance, `examples/device1.json` in the Python project.
98     #[arg(long)]
99     device_config: path::PathBuf,
100 
101     /// Bumble transport spec.
102     ///
103     /// <https://google.github.io/bumble/transports/index.html>
104     #[arg(long)]
105     transport: String,
106 
107     /// Whether to perform an extended (BT 5.0) advertisement
108     #[arg(long)]
109     extended: bool,
110 
111     /// Log HCI commands
112     #[arg(long)]
113     log_hci: bool,
114 }
115