Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions examples/monitor_signal_strength.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use clap::Parser;
use iwdrs::station::signal_level_agent::SignalLevelAgent;

#[derive(Debug, Parser)]
/// Connect to a Wifi Network given a SSID and optionally a password.
struct Args {
#[clap(allow_hyphen_values = true)]
levels: Vec<i16>,
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
let Args { levels } = Args::parse();

let session = iwdrs::session::Session::new().await.unwrap();

let station = session.stations().pop().unwrap();

station
.register_signal_level_agent(levels, Agent {})
.await
.unwrap();

std::future::pending::<()>().await;
}

struct Agent {}

impl SignalLevelAgent for Agent {
fn changed(
&self,
_station: &iwdrs::station::Station,
signal_level: impl std::ops::RangeBounds<i16>,
) {
println!(
"Wifi Signal Strength Min {:?} Max {:?}",
signal_level.start_bound(),
signal_level.end_bound()
)
}
}
34 changes: 33 additions & 1 deletion src/station.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashMap, str::FromStr, sync::Arc};
use std::{cmp::Reverse, collections::HashMap, str::FromStr, sync::Arc};

use futures_lite::{Stream, StreamExt};
use strum::EnumString;
Expand All @@ -14,6 +14,9 @@ use crate::{
network::Network,
};

use signal_level_agent::SignalLevelAgentManager;
pub mod signal_level_agent;

#[derive(Debug, Clone)]
pub struct Station {
pub(crate) connection: Arc<Connection>,
Expand Down Expand Up @@ -105,6 +108,35 @@ impl Station {

Ok(networks)
}

/// Register the agent object to receive signal strength level change notifications on the provided agent.
/// The "levels" parameters decides the thresholds in dBm that will generate a call to the
/// [`SignalLevelAgent::changed`] method whenever current RSSI crosses any of the values. The number and distance between
/// requested threshold values is a compromise between resolution and the frequency of system wakeups and
/// context-switches that are going to be occurring to update the client's signal meter. Only one agent
/// can be registered at any time.
pub async fn register_signal_level_agent(
&self,
mut levels: Vec<i16>,
agent: impl signal_level_agent::SignalLevelAgent,
) -> zbus::Result<SignalLevelAgentManager> {
// Signal level boundaries should be sorted
levels.sort_by_key(|signal_level| Reverse(*signal_level));

let interface = signal_level_agent::SignalLevelInterface {
agent,
connection: self.connection.clone(),
levels: levels.clone(),
};

let manager = SignalLevelAgentManager::register_agent(self.clone(), interface).await?;

let proxy = self.proxy().await?;
proxy
.call_method("RegisterSignalLevelAgent", &(&manager.dbus_path, levels))
.await?;
Ok(manager)
}
}

impl StationDiagnostics {
Expand Down
97 changes: 97 additions & 0 deletions src/station/signal_level_agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use std::{
ops::{Bound, RangeBounds},
sync::Arc,
};

use uuid::Uuid;
use zbus::{Connection, interface};
use zvariant::OwnedObjectPath;

use crate::station::Station;

pub trait SignalLevelAgent: Send + Sync + 'static {
/// This method gets called when the service daemon unregisters the agent. An agent can use it to do
/// cleanup tasks. There is no need to unregister the agent, because when this method gets called it has
/// already been unregistered.
fn release(&self) {}

fn changed(&self, station: &Station, signal_level: impl RangeBounds<i16>);
}

pub struct SignalLevelInterface<A> {
pub(super) agent: A,
pub(super) connection: Arc<Connection>,
pub(super) levels: Vec<i16>,
}

#[interface(name = "net.connman.iwd.SignalLevelAgent")]
impl<A: SignalLevelAgent> SignalLevelInterface<A> {
#[zbus(name = "Release")]
fn release(&self) {
self.agent.release();
}

/// This method gets called when the signal strength measurement for the device's connected network changes
/// enough to go from one level to another out of the N ranges defined by the array of (N-1) threshold values
/// passed to RegisterSignalLevelAgent(). It also gets registered. The level parameter is in the range from 0
/// called immediately after the signal level agent is to N, 0 being the strongest signal or above the first
/// threshold value in the array, and N being the weakest and below the last threshold value. For example if
/// RegisterSignalLevelAgent was called with the array [-40, -50, -60], the 'level' parameter of 0 would mean signal
/// is received at -40 or more dBm and 3 would mean below -60 dBm and might correspond to 1 out of 4 bars on a UI
/// signal meter.
#[zbus(name = "Changed")]
fn changed(&self, station_path: OwnedObjectPath, level_idx: u8) {
let station = Station {
connection: self.connection.clone(),
dbus_path: station_path,
};

let level_idx = usize::from(level_idx);

let max_strength = level_idx
.checked_sub(1)
.and_then(|level| self.levels.get(level))
.map(|level| Bound::Excluded(*level))
.unwrap_or(Bound::Unbounded);
let min_strength = self
.levels
.get(level_idx)
.map(|level| Bound::Included(*level))
.unwrap_or(Bound::Unbounded);

self.agent.changed(&station, (min_strength, max_strength))
}
}

pub struct SignalLevelAgentManager {
pub(crate) dbus_path: OwnedObjectPath,
pub(crate) station: super::Station,
}

impl SignalLevelAgentManager {
pub(crate) async fn register_agent(
station: super::Station,
interface: SignalLevelInterface<impl SignalLevelAgent>,
) -> zbus::Result<Self> {
let dbus_path = OwnedObjectPath::try_from(format!(
"/iwdrs/signal_level_agent/{}",
Uuid::new_v4().as_simple()
))?;
station
.connection
.object_server()
.at(dbus_path.clone(), interface)
.await?;

Ok(Self { dbus_path, station })
}

pub async fn unregister(self) -> zbus::Result<()> {
let proxy = self.station.proxy().await?;

proxy
.call_method("UnregisterSignalLevelAgent", &(&self.dbus_path,))
.await?;
Ok(())
}
}