Start using zbus instead of dbus
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
82019d17d9
commit
4bf5204ad4
7 changed files with 458 additions and 316 deletions
18
Cargo.toml
18
Cargo.toml
|
@ -7,20 +7,8 @@ edition = "2021"
|
|||
categories = ["gateway", "bridge", "transport", "modemmanager", "xmpp", "jabber"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.56"
|
||||
mmdbus = "1.18.6"
|
||||
dbus-tokio = "0.7.5"
|
||||
tokio = { version = "1.19.2", features = [ "time", "macros", "rt-multi-thread" ] }
|
||||
tokio = { version = "1", features = [ "macros", "rt-multi-thread" ] }
|
||||
futures = "0.3"
|
||||
futures-util = "0.3"
|
||||
xmpp = "0.3"
|
||||
xmpp-parsers = "0.19"
|
||||
log = "0.4"
|
||||
env_logger = "0.8"
|
||||
|
||||
[patch.crates-io]
|
||||
jid = { path = "../xmpp-rs/jid" }
|
||||
minidom = { path = "../xmpp-rs/minidom" }
|
||||
tokio-xmpp = { path = "../xmpp-rs/tokio-xmpp" }
|
||||
xmpp = { path = "../xmpp-rs/xmpp" }
|
||||
xmpp-parsers = { path = "../xmpp-rs/parsers" }
|
||||
pretty_env_logger = "0.5"
|
||||
zbus = "3.14.1"
|
||||
|
|
30
src/error.rs
30
src/error.rs
|
@ -18,22 +18,17 @@
|
|||
use std::convert::From;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
use mmdbus::dbus;
|
||||
use xmpp_parsers::JidParseError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
DBusError(dbus::Error),
|
||||
InvalidArgs,
|
||||
JidParseError(JidParseError),
|
||||
Zbus(zbus::Error),
|
||||
ZbusFdo(zbus::fdo::Error),
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn cause(&self) -> Option<&dyn StdError> {
|
||||
match self {
|
||||
Error::DBusError(e) => Some(e),
|
||||
Error::InvalidArgs => None,
|
||||
Error::JidParseError(e) => Some(e),
|
||||
Error::Zbus(e) => Some(e),
|
||||
Error::ZbusFdo(e) => Some(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,21 +36,20 @@ impl StdError for Error {
|
|||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::DBusError(e) => write!(fmt, "dbus error: {}", e),
|
||||
Error::InvalidArgs => write!(fmt, "invalid arguments"),
|
||||
Error::JidParseError(e) => write!(fmt, "jid parse error: {}", e),
|
||||
Error::Zbus(e) => write!(fmt, "zbus error: {}", e),
|
||||
Error::ZbusFdo(e) => write!(fmt, "zbus fdo error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<dbus::Error> for Error {
|
||||
fn from(err: dbus::Error) -> Error {
|
||||
Error::DBusError(err)
|
||||
impl From<zbus::Error> for Error {
|
||||
fn from(err: zbus::Error) -> Error {
|
||||
Error::Zbus(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JidParseError> for Error {
|
||||
fn from(err: JidParseError) -> Error {
|
||||
Error::JidParseError(err)
|
||||
impl From<zbus::fdo::Error> for Error {
|
||||
fn from(err: zbus::fdo::Error) -> Error {
|
||||
Error::ZbusFdo(err)
|
||||
}
|
||||
}
|
||||
|
|
236
src/main.rs
236
src/main.rs
|
@ -13,233 +13,19 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod error;
|
||||
pub mod mm;
|
||||
mod error;
|
||||
mod modem;
|
||||
|
||||
use error::Error;
|
||||
use mm::modem::Modem as ModemAccess;
|
||||
use crate::error::Error;
|
||||
use crate::modem::ModemManager;
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use std::env::args;
|
||||
|
||||
use env_logger;
|
||||
|
||||
use futures_util::StreamExt;
|
||||
use tokio::{
|
||||
task::JoinHandle,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use mmdbus::{
|
||||
dbus::{
|
||||
message::MatchRule,
|
||||
nonblock::{
|
||||
SyncConnection, Proxy,
|
||||
stdintf::org_freedesktop_dbus::ObjectManager,
|
||||
},
|
||||
strings::{Path as DBusPath},
|
||||
},
|
||||
};
|
||||
|
||||
use dbus_tokio::connection;
|
||||
|
||||
use xmpp::{ClientBuilder, ClientType};
|
||||
use xmpp_parsers::{
|
||||
Jid, BareJid,
|
||||
message::MessageType,
|
||||
};
|
||||
|
||||
// The following source has been used as an example:
|
||||
// https://github.com/soerenmeier/linux-info/blob/master/src/network/modem_manager.rs
|
||||
|
||||
const MM_NAME: &str = "org.freedesktop.ModemManager1";
|
||||
const MM_PATH: &str = "/org/freedesktop/ModemManager1";
|
||||
const TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DBus {
|
||||
pub conn: Arc<SyncConnection>,
|
||||
pub handle: Arc<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl DBus {
|
||||
pub fn connect() -> Result<Self, Error> {
|
||||
let (resource, conn) = connection::new_system_sync()?;
|
||||
|
||||
let handle: JoinHandle<()> = tokio::spawn(async {
|
||||
let err = resource.await;
|
||||
panic!("Lost connection to D-Bus: {}", err);
|
||||
});
|
||||
|
||||
Ok(Self { conn, handle: Arc::new(handle) })
|
||||
}
|
||||
|
||||
pub fn proxy<'a, 'b, P>(&'b self, path: P) -> Proxy<'a, &'b SyncConnection>
|
||||
where
|
||||
P: Into<DBusPath<'a>>,
|
||||
{
|
||||
Proxy::new(MM_NAME, path, TIMEOUT, &*self.conn)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModemManager {
|
||||
dbus: DBus,
|
||||
}
|
||||
|
||||
impl ModemManager {
|
||||
pub fn connect() -> Result<Self, Error> {
|
||||
DBus::connect().map(|dbus| Self { dbus })
|
||||
}
|
||||
|
||||
pub async fn modems(&self) -> Result<Vec<Modem>, Error> {
|
||||
let objects = self.dbus.proxy(MM_PATH).get_managed_objects().await?;
|
||||
let modems = objects
|
||||
.into_iter()
|
||||
.map(|(path, _)| {
|
||||
Modem {
|
||||
dbus: self.dbus.clone(),
|
||||
path,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(modems)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Modem {
|
||||
dbus: DBus,
|
||||
pub path: DBusPath<'static>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Modem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Modem {{ {}, {:?}, .. }}", self.path, "1234")
|
||||
}
|
||||
}
|
||||
|
||||
impl Modem {
|
||||
pub async fn enabled(&self) -> Result<bool, Error> {
|
||||
let state: ModemState = ModemAccess::state(&self.dbus.proxy(&self.path)).await.map(Into::into)?;
|
||||
Ok(match state {
|
||||
ModemState::Enabling
|
||||
| ModemState::Enabled
|
||||
| ModemState::Searching
|
||||
| ModemState::Registered
|
||||
| ModemState::Connecting
|
||||
| ModemState::Connected => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn model(&self) -> Result<String, Error> {
|
||||
ModemAccess::model(&self.dbus.proxy(&self.path)).await.map_err(Error::DBusError)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ModemState {
|
||||
/// The modem is unusable.
|
||||
Failed = -1,
|
||||
/// State unknown or not reportable.
|
||||
Unknown = 0,
|
||||
/// The modem is currently being initialized.
|
||||
Initializing = 1,
|
||||
/// The modem needs to be unlocked.
|
||||
Locked = 2,
|
||||
/// The modem is not enabled and is powered down.
|
||||
Disabled = 3,
|
||||
/// The modem is currently transitioning to the MM_MODEM_STATE_DISABLED
|
||||
/// state.
|
||||
Disabling = 4,
|
||||
/// The modem is currently transitioning to the MM_MODEM_STATE_ENABLED
|
||||
/// state.
|
||||
Enabling = 5,
|
||||
/// The modem is enabled and powered on but not registered with a network
|
||||
/// provider and not available for data connections.
|
||||
Enabled = 6,
|
||||
/// The modem is searching for a network provider to register with.
|
||||
Searching = 7,
|
||||
/// The modem is registered with a network provider, and data connections
|
||||
/// and messaging may be available for use.
|
||||
Registered = 8,
|
||||
/// The modem is disconnecting and deactivating the last active packet data
|
||||
/// bearer. This state will not be entered if more than one packet data
|
||||
/// bearer is active and one of the active bearers is deactivated.
|
||||
Disconnecting = 9,
|
||||
/// The modem is activating and connecting the first packet data bearer.
|
||||
/// Subsequent bearer activations when another bearer is already active
|
||||
/// do not cause this state to be entered.
|
||||
Connecting = 10,
|
||||
/// One or more packet data bearers is active and connected.
|
||||
Connected = 11,
|
||||
}
|
||||
|
||||
impl From<i32> for ModemState {
|
||||
fn from(num: i32) -> Self {
|
||||
if num < -1 || num > 11 {
|
||||
Self::Unknown
|
||||
} else {
|
||||
unsafe { *(&num as *const i32 as *const Self) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn print_foo(mm: ModemManager) -> Result<(), Error> {
|
||||
let modems = mm.modems().await?;
|
||||
println!("Modems: {:?}", modems);
|
||||
for modem in modems {
|
||||
println!("Modem: {:?}", modem);
|
||||
println!("Enabled: {:?}", modem.enabled().await);
|
||||
println!("Model: {:?}", modem.model().await);
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
let manager = ModemManager::connect().await?;
|
||||
for modem in manager {
|
||||
println!("FOO0: {:?}", modem);
|
||||
println!("FOO0: {:?}", modem.manufacturer().await?);
|
||||
println!("FOO0: {:?}", modem.model().await?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<(), Error> {
|
||||
env_logger::init();
|
||||
|
||||
let args: Vec<String> = args().collect();
|
||||
if args.len() != 3 {
|
||||
println!("Usage: {} <jid> <password>", args[0]);
|
||||
return Err(Error::InvalidArgs)
|
||||
}
|
||||
|
||||
// ModemManager
|
||||
let dbus = DBus::connect()?;
|
||||
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(2));
|
||||
|
||||
let mr = MatchRule::new_signal("org.freedesktop.DBus.Properties", "PropertiesChanged");
|
||||
|
||||
|
||||
// XMPP
|
||||
let jid_str = args[1].as_str();
|
||||
let jid = Jid::Bare(BareJid::from_str(jid_str).unwrap());
|
||||
let passwd = args[2].as_str();
|
||||
|
||||
// Client instance
|
||||
let mut xmpp = ClientBuilder::new(jid_str, passwd)
|
||||
.set_client(ClientType::Bot, "gateway_mm")
|
||||
.set_website("https://codeberg.org/pep./gateway_mm")
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
events = xmpp.wait_for_events() => {
|
||||
println!("Events: {:?}", events);
|
||||
},
|
||||
_ = interval.tick() => {
|
||||
let mm = ModemManager { dbus: dbus.clone() };
|
||||
let _ = print_foo(mm).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Affero General Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License, or (at your
|
||||
// option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
||||
// for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod modem;
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Affero General Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License, or (at your
|
||||
// option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
||||
// for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::marker::Sync;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mmdbus::dbus::{nonblock, Error as DBusError};
|
||||
|
||||
#[async_trait]
|
||||
pub trait Modem {
|
||||
async fn enable(&self, enable: bool) -> Result<(), DBusError>;
|
||||
async fn state(&self) -> Result<i32, DBusError>;
|
||||
async fn model(&self) -> Result<String, DBusError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<'a, T: nonblock::NonblockReply, C: Deref<Target=T> + Sync> Modem for nonblock::Proxy<'a, C> {
|
||||
async fn enable(&self, enable: bool) -> Result<(), DBusError> {
|
||||
self.method_call("org.freedesktop.ModemManager1.Modem", "Enable", (enable,)).await
|
||||
}
|
||||
|
||||
async fn state(&self) -> Result<i32, DBusError> {
|
||||
<Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.ModemManager1.Modem", "State").await
|
||||
}
|
||||
|
||||
async fn model(&self) -> Result<String, DBusError> {
|
||||
<Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.ModemManager1.Modem", "Model").await
|
||||
}
|
||||
}
|
316
src/modem/introspect.rs
Normal file
316
src/modem/introspect.rs
Normal file
|
@ -0,0 +1,316 @@
|
|||
//! DBus interface proxies for:
|
||||
//!
|
||||
//! - `org.freedesktop.ModemManager1.Modem`,
|
||||
//! - `org.freedesktop.ModemManager1.Modem.Messaging`,
|
||||
//! - `org.freedesktop.ModemManager1.Modem.Simple`,
|
||||
//! - `org.freedesktop.ModemManager1.Modem.Voice`
|
||||
//!
|
||||
//! This code was generated by `zbus-xmlgen` `3.1.1` from DBus introspection data.
|
||||
//! Source: `ModemManager1.xml`.
|
||||
|
||||
use zbus::dbus_proxy;
|
||||
|
||||
#[dbus_proxy(
|
||||
interface = "org.freedesktop.ModemManager1.Modem.Messaging",
|
||||
assume_defaults = true
|
||||
)]
|
||||
trait Messaging {
|
||||
/// Create method
|
||||
fn create(
|
||||
&self,
|
||||
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
|
||||
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
|
||||
|
||||
/// Delete method
|
||||
fn delete(&self, path: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// List method
|
||||
fn list(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// Added signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn added(&self, path: zbus::zvariant::ObjectPath<'_>, received: bool) -> zbus::Result<()>;
|
||||
|
||||
/// Deleted signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn deleted(&self, path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// DefaultStorage property
|
||||
#[dbus_proxy(property)]
|
||||
fn default_storage(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// Messages property
|
||||
#[dbus_proxy(property)]
|
||||
fn messages(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// SupportedStorages property
|
||||
#[dbus_proxy(property)]
|
||||
fn supported_storages(&self) -> zbus::Result<Vec<u32>>;
|
||||
}
|
||||
|
||||
#[dbus_proxy(
|
||||
interface = "org.freedesktop.ModemManager1.Modem",
|
||||
assume_defaults = true
|
||||
)]
|
||||
trait Modem {
|
||||
/// Command method
|
||||
fn command(&self, cmd: &str, timeout: u32) -> zbus::Result<String>;
|
||||
|
||||
/// CreateBearer method
|
||||
fn create_bearer(
|
||||
&self,
|
||||
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
|
||||
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
|
||||
|
||||
/// DeleteBearer method
|
||||
fn delete_bearer(&self, bearer: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// Enable method
|
||||
fn enable(&self, enable: bool) -> zbus::Result<()>;
|
||||
|
||||
/// FactoryReset method
|
||||
fn factory_reset(&self, code: &str) -> zbus::Result<()>;
|
||||
|
||||
/// GetCellInfo method
|
||||
fn get_cell_info(
|
||||
&self,
|
||||
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
|
||||
|
||||
/// ListBearers method
|
||||
fn list_bearers(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// Reset method
|
||||
fn reset(&self) -> zbus::Result<()>;
|
||||
|
||||
/// SetCurrentBands method
|
||||
fn set_current_bands(&self, bands: &[u32]) -> zbus::Result<()>;
|
||||
|
||||
/// SetCurrentCapabilities method
|
||||
fn set_current_capabilities(&self, capabilities: u32) -> zbus::Result<()>;
|
||||
|
||||
/// SetCurrentModes method
|
||||
fn set_current_modes(&self, modes: &(u32, u32)) -> zbus::Result<()>;
|
||||
|
||||
/// SetPowerState method
|
||||
fn set_power_state(&self, state: u32) -> zbus::Result<()>;
|
||||
|
||||
/// SetPrimarySimSlot method
|
||||
fn set_primary_sim_slot(&self, sim_slot: u32) -> zbus::Result<()>;
|
||||
|
||||
// TODO: This fails to compile: duplicate definitions for `receive_state_changed`
|
||||
// /// StateChanged signal
|
||||
// #[dbus_proxy(signal)]
|
||||
// fn state_changed(&self, old: i32, new: i32, reason: u32) -> zbus::Result<()>;
|
||||
|
||||
/// AccessTechnologies property
|
||||
#[dbus_proxy(property)]
|
||||
fn access_technologies(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// Bearers property
|
||||
#[dbus_proxy(property)]
|
||||
fn bearers(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// CarrierConfiguration property
|
||||
#[dbus_proxy(property)]
|
||||
fn carrier_configuration(&self) -> zbus::Result<String>;
|
||||
|
||||
/// CarrierConfigurationRevision property
|
||||
#[dbus_proxy(property)]
|
||||
fn carrier_configuration_revision(&self) -> zbus::Result<String>;
|
||||
|
||||
/// CurrentBands property
|
||||
#[dbus_proxy(property)]
|
||||
fn current_bands(&self) -> zbus::Result<Vec<u32>>;
|
||||
|
||||
/// CurrentCapabilities property
|
||||
#[dbus_proxy(property)]
|
||||
fn current_capabilities(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// CurrentModes property
|
||||
#[dbus_proxy(property)]
|
||||
fn current_modes(&self) -> zbus::Result<(u32, u32)>;
|
||||
|
||||
/// Device property
|
||||
#[dbus_proxy(property)]
|
||||
fn device(&self) -> zbus::Result<String>;
|
||||
|
||||
/// DeviceIdentifier property
|
||||
#[dbus_proxy(property)]
|
||||
fn device_identifier(&self) -> zbus::Result<String>;
|
||||
|
||||
/// Drivers property
|
||||
#[dbus_proxy(property)]
|
||||
fn drivers(&self) -> zbus::Result<Vec<String>>;
|
||||
|
||||
/// EquipmentIdentifier property
|
||||
#[dbus_proxy(property)]
|
||||
fn equipment_identifier(&self) -> zbus::Result<String>;
|
||||
|
||||
/// HardwareRevision property
|
||||
#[dbus_proxy(property)]
|
||||
fn hardware_revision(&self) -> zbus::Result<String>;
|
||||
|
||||
/// Manufacturer property
|
||||
#[dbus_proxy(property)]
|
||||
fn manufacturer(&self) -> zbus::Result<String>;
|
||||
|
||||
/// MaxActiveBearers property
|
||||
#[dbus_proxy(property)]
|
||||
fn max_active_bearers(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// MaxActiveMultiplexedBearers property
|
||||
#[dbus_proxy(property)]
|
||||
fn max_active_multiplexed_bearers(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// MaxBearers property
|
||||
#[dbus_proxy(property)]
|
||||
fn max_bearers(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// Model property
|
||||
#[dbus_proxy(property)]
|
||||
fn model(&self) -> zbus::Result<String>;
|
||||
|
||||
/// OwnNumbers property
|
||||
#[dbus_proxy(property)]
|
||||
fn own_numbers(&self) -> zbus::Result<Vec<String>>;
|
||||
|
||||
/// Plugin property
|
||||
#[dbus_proxy(property)]
|
||||
fn plugin(&self) -> zbus::Result<String>;
|
||||
|
||||
/// Ports property
|
||||
#[dbus_proxy(property)]
|
||||
fn ports(&self) -> zbus::Result<Vec<(String, u32)>>;
|
||||
|
||||
/// PowerState property
|
||||
#[dbus_proxy(property)]
|
||||
fn power_state(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// PrimaryPort property
|
||||
#[dbus_proxy(property)]
|
||||
fn primary_port(&self) -> zbus::Result<String>;
|
||||
|
||||
/// PrimarySimSlot property
|
||||
#[dbus_proxy(property)]
|
||||
fn primary_sim_slot(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// Revision property
|
||||
#[dbus_proxy(property)]
|
||||
fn revision(&self) -> zbus::Result<String>;
|
||||
|
||||
/// SignalQuality property
|
||||
#[dbus_proxy(property)]
|
||||
fn signal_quality(&self) -> zbus::Result<(u32, bool)>;
|
||||
|
||||
/// Sim property
|
||||
#[dbus_proxy(property)]
|
||||
fn sim(&self) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
|
||||
|
||||
/// SimSlots property
|
||||
#[dbus_proxy(property)]
|
||||
fn sim_slots(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// State property
|
||||
#[dbus_proxy(property)]
|
||||
fn state(&self) -> zbus::Result<i32>;
|
||||
|
||||
/// StateFailedReason property
|
||||
#[dbus_proxy(property)]
|
||||
fn state_failed_reason(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// SupportedBands property
|
||||
#[dbus_proxy(property)]
|
||||
fn supported_bands(&self) -> zbus::Result<Vec<u32>>;
|
||||
|
||||
/// SupportedCapabilities property
|
||||
#[dbus_proxy(property)]
|
||||
fn supported_capabilities(&self) -> zbus::Result<Vec<u32>>;
|
||||
|
||||
/// SupportedIpFamilies property
|
||||
#[dbus_proxy(property)]
|
||||
fn supported_ip_families(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// SupportedModes property
|
||||
#[dbus_proxy(property)]
|
||||
fn supported_modes(&self) -> zbus::Result<Vec<(u32, u32)>>;
|
||||
|
||||
/// UnlockRequired property
|
||||
#[dbus_proxy(property)]
|
||||
fn unlock_required(&self) -> zbus::Result<u32>;
|
||||
|
||||
/// UnlockRetries property
|
||||
#[dbus_proxy(property)]
|
||||
fn unlock_retries(&self) -> zbus::Result<std::collections::HashMap<u32, u32>>;
|
||||
}
|
||||
|
||||
#[dbus_proxy(
|
||||
interface = "org.freedesktop.ModemManager1.Modem.Simple",
|
||||
assume_defaults = true
|
||||
)]
|
||||
trait Simple {
|
||||
/// Connect method
|
||||
fn connect(
|
||||
&self,
|
||||
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
|
||||
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
|
||||
|
||||
/// Disconnect method
|
||||
fn disconnect(&self, bearer: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// GetStatus method
|
||||
fn get_status(
|
||||
&self,
|
||||
) -> zbus::Result<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>;
|
||||
}
|
||||
|
||||
#[dbus_proxy(
|
||||
interface = "org.freedesktop.ModemManager1.Modem.Voice",
|
||||
assume_defaults = true
|
||||
)]
|
||||
trait Voice {
|
||||
/// CallWaitingQuery method
|
||||
fn call_waiting_query(&self) -> zbus::Result<bool>;
|
||||
|
||||
/// CallWaitingSetup method
|
||||
fn call_waiting_setup(&self, enable: bool) -> zbus::Result<()>;
|
||||
|
||||
/// CreateCall method
|
||||
fn create_call(
|
||||
&self,
|
||||
properties: std::collections::HashMap<&str, zbus::zvariant::Value<'_>>,
|
||||
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;
|
||||
|
||||
/// DeleteCall method
|
||||
fn delete_call(&self, path: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// HangupAll method
|
||||
fn hangup_all(&self) -> zbus::Result<()>;
|
||||
|
||||
/// HangupAndAccept method
|
||||
fn hangup_and_accept(&self) -> zbus::Result<()>;
|
||||
|
||||
/// HoldAndAccept method
|
||||
fn hold_and_accept(&self) -> zbus::Result<()>;
|
||||
|
||||
/// ListCalls method
|
||||
fn list_calls(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// Transfer method
|
||||
fn transfer(&self) -> zbus::Result<()>;
|
||||
|
||||
/// CallAdded signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn call_added(&self, path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// CallDeleted signal
|
||||
#[dbus_proxy(signal)]
|
||||
fn call_deleted(&self, path: zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;
|
||||
|
||||
/// Calls property
|
||||
#[dbus_proxy(property)]
|
||||
fn calls(&self) -> zbus::Result<Vec<zbus::zvariant::OwnedObjectPath>>;
|
||||
|
||||
/// EmergencyOnly property
|
||||
#[dbus_proxy(property)]
|
||||
fn emergency_only(&self) -> zbus::Result<bool>;
|
||||
}
|
116
src/modem/mod.rs
Normal file
116
src/modem/mod.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright (C) 2022 Maxime “pep” Buquet <pep@bouah.net>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Affero General Public License as published by the
|
||||
// Free Software Foundation, either version 3 of the License, or (at your
|
||||
// option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
||||
// for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
mod introspect;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::modem::introspect::ModemProxy;
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use futures::future::join_all;
|
||||
use zbus::{fdo::ObjectManagerProxy, zvariant::ObjectPath, Connection};
|
||||
|
||||
const MM_NAME: &str = "org.freedesktop.ModemManager1";
|
||||
const MM_PATH: &str = "/org/freedesktop/ModemManager1";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModemManager<'a> {
|
||||
connection: Connection,
|
||||
modems: Vec<Modem<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> ModemManager<'a> {
|
||||
pub async fn connect() -> Result<ModemManager<'a>, Error> {
|
||||
let connection = Connection::system().await?;
|
||||
Ok(ModemManager {
|
||||
modems: ModemManager::modems(&connection).await?,
|
||||
connection,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn modems(conn: &Connection) -> Result<Vec<Modem<'a>>, Error> {
|
||||
let proxy = ObjectManagerProxy::builder(&conn)
|
||||
.destination(MM_NAME)?
|
||||
.path(MM_PATH)?
|
||||
.build()
|
||||
.await?;
|
||||
let modems = proxy
|
||||
.get_managed_objects()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|(path, _)| Modem::new(&conn, path.into()))
|
||||
.collect::<Vec<_>>();
|
||||
join_all(modems)
|
||||
.await
|
||||
.into_iter()
|
||||
.try_fold(vec![], |mut acc, res| {
|
||||
let modem = res?;
|
||||
acc.push(modem);
|
||||
Ok::<Vec<Modem<'_>>, Error>(acc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ModemManager<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ModemManager")
|
||||
.field("modems", &self.modems)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for ModemManager<'a> {
|
||||
type Item = Modem<'a>;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.modems.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Modem<'a>(ModemProxy<'a>);
|
||||
|
||||
impl<'a> Modem<'a> {
|
||||
pub async fn new(conn: &Connection, path: ObjectPath<'a>) -> Result<Modem<'a>, Error> {
|
||||
Ok(Modem(ModemProxy::builder(&conn).path(path)?.build().await?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Modem<'a> {
|
||||
type Target = ModemProxy<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for Modem<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Modem<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Modem")
|
||||
.field("destination", &self.inner().destination())
|
||||
.field("path", &self.inner().path())
|
||||
.field("interface", &self.inner().interface())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue