| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! Counterpart to the Python example `battery_server.py`. |
| //! |
| //! Start an Android emulator from Android Studio, or otherwise have netsim running. |
| //! |
| //! Run the server from the project root: |
| //! ``` |
| //! PYTHONPATH=. python examples/battery_server.py \ |
| //! examples/device1.json android-netsim |
| //! ``` |
| //! |
| //! Then run this example from the `rust` directory: |
| //! |
| //! ``` |
| //! PYTHONPATH=..:/path/to/virtualenv/site-packages/ \ |
| //! cargo run --example battery_client -- \ |
| //! --transport android-netsim \ |
| //! --target-addr F0:F1:F2:F3:F4:F5 |
| //! ``` |
| |
| use bumble::wrapper::{ |
| device::{Device, Peer}, |
| profile::BatteryServiceProxy, |
| transport::Transport, |
| PyObjectExt, |
| }; |
| use clap::Parser as _; |
| use log::info; |
| use owo_colors::OwoColorize; |
| use pyo3::prelude::*; |
| |
| #[pyo3_asyncio::tokio::main] |
| async fn main() -> PyResult<()> { |
| env_logger::builder() |
| .filter_level(log::LevelFilter::Info) |
| .init(); |
| |
| let cli = Cli::parse(); |
| |
| let transport = Transport::open(cli.transport).await?; |
| |
| let device = Device::with_hci( |
| "Bumble", |
| "F0:F1:F2:F3:F4:F5", |
| transport.source()?, |
| transport.sink()?, |
| )?; |
| |
| device.power_on().await?; |
| |
| let conn = device.connect(&cli.target_addr).await?; |
| let mut peer = Peer::new(conn)?; |
| for mut s in peer.discover_services().await? { |
| s.discover_characteristics().await?; |
| } |
| let battery_service = peer |
| .create_service_proxy::<BatteryServiceProxy>()? |
| .ok_or(anyhow::anyhow!("No battery service found"))?; |
| |
| let mut battery_level_char = battery_service |
| .battery_level()? |
| .ok_or(anyhow::anyhow!("No battery level characteristic"))?; |
| info!( |
| "{} {}", |
| "Initial Battery Level:".green(), |
| battery_level_char |
| .read_value() |
| .await? |
| .extract_with_gil::<u32>()? |
| ); |
| battery_level_char |
| .subscribe(|_py, args| { |
| info!( |
| "{} {:?}", |
| "Battery level update:".green(), |
| args.get_item(0)?.extract::<u32>()?, |
| ); |
| Ok(()) |
| }) |
| .await?; |
| |
| // wait until user kills the process |
| tokio::signal::ctrl_c().await?; |
| Ok(()) |
| } |
| |
| #[derive(clap::Parser)] |
| #[command(author, version, about, long_about = None)] |
| struct Cli { |
| /// Bumble transport spec. |
| /// |
| /// <https://google.github.io/bumble/transports/index.html> |
| #[arg(long)] |
| transport: String, |
| |
| /// Address to connect to |
| #[arg(long)] |
| target_addr: String, |
| } |