Skip to content

feat: high level ModemManager API #419

Open
cachebag wants to merge 2 commits into
masterfrom
mmrs-api
Open

feat: high level ModemManager API #419
cachebag wants to merge 2 commits into
masterfrom
mmrs-api

Conversation

@cachebag
Copy link
Copy Markdown
Collaborator

Closes #402

@cachebag cachebag self-assigned this May 15, 2026
@cachebag cachebag added api-surface Public API design, re-exports, and semver-relevant changes dbus D-Bus types, signatures, or NM D-Bus API zbus zbus API surface mmrs modemmanager bindings crate labels May 15, 2026
@cachebag cachebag requested a review from Copilot May 15, 2026 22:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces the high-level ModemManager entry point and per-modem ModemScope over the existing low-level zbus proxies, fulfilling the API surface described in issue #402. Adds a new ConnectionStatus model, wires zbus::fdo::Error into ModemError, re-exports the new types from the crate root, and updates the crate-level docs and changelog.

Changes:

  • New api::modem_manager module implementing enumeration via ObjectManager.GetManagedObjects, primary/by-IMEI selection, simple connect/disconnect/status, SIM PIN flows, signal/access-tech queries, and property-decoding helpers.
  • New api::modem_scope::ModemScope<'a> that forwards every operation to crate-private *_for_path helpers on ModemManager.
  • Adds ConnectionStatus model, ModemError::DbusFdo variant, re-exports, and refreshed crate-level docs/example plus a changelog entry.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
mmrs/src/api/modem_manager.rs Core implementation of the high-level API and D-Bus property decoders.
mmrs/src/api/modem_scope.rs Per-modem scoped wrapper that delegates to ModemManager helpers.
mmrs/src/api/mod.rs Wires the two new modules into the API surface and re-exports the types.
mmrs/src/api/models/modem.rs Adds the ConnectionStatus snapshot struct returned by status helpers.
mmrs/src/api/models/mod.rs Re-exports the new ConnectionStatus type.
mmrs/src/api/models/error.rs Adds ModemError::DbusFdo so zbus::fdo::Error propagates via ?.
mmrs/src/lib.rs Updates crate-level docs, doctest example, and re-exports.
mmrs/CHANGELOG.md Documents the new high-level API and ConnectionStatus under Unreleased.

Comment on lines +533 to +539

fn is_wrong_pin_error(error: &zbus::Error) -> bool {
let rendered = error.to_string().to_ascii_lowercase();
rendered.contains("wrong") && rendered.contains("pin")
}

fn is_wrong_puk_error(error: &zbus::Error) -> bool {

/// Creates a [`ModemManager`] from an existing D-Bus connection.
pub async fn with_connection(conn: Connection) -> Result<Self> {
MMManagerProxy::new(&conn).await?;
Comment on lines +431 to +432
OwnedObjectPath::try_from(path)
.map_err(|e| ModemError::ModemNotFound(format!("{path} (invalid D-Bus object path: {e})")))
Comment on lines +246 to +256
let signal_quality = take_u32(&status, "signal-quality").or(Some(modem.signal_quality));

Ok(ConnectionStatus {
modem_path: modem.path,
state,
connected: state.is_connected(),
access_technology,
signal_quality,
bearer_paths: modem.bearer_paths,
})
}
Comment on lines +48 to +57
pub async fn list_modems(&self) -> Result<Vec<Modem>> {
let paths = enumerate_modem_paths(&self.conn).await?;
let mut modems = Vec::with_capacity(paths.len());

for path in paths {
modems.push(self.modem_info_for_path(path.as_str()).await?);
}

Ok(modems)
}
Comment on lines +440 to +453
fn decode_ip4_config(values: &HashMap<String, OwnedValue>) -> Option<Ip4Config> {
if values.is_empty() {
return None;
}

Some(Ip4Config {
method: take_str(values, "method")
.or_else(|| take_u32(values, "method").map(|value| value.to_string()))
.unwrap_or_default(),
address: take_str(values, "address").and_then(|value| value.parse().ok()),
prefix: take_u32(values, "prefix").unwrap_or_default(),
gateway: take_str(values, "gateway").and_then(|value| value.parse().ok()),
dns: take_ipv4_vec(values, "dns"),
mtu: take_u32(values, "mtu"),
Comment on lines +61 to +65
self.list_modems()
.await?
.into_iter()
.find(|modem| modem.equipment_identifier == imei)
.ok_or_else(|| ModemError::ModemNotFound(format!("IMEI {imei}")))
Comment on lines +279 to +331
pub(crate) async fn unlock_pin_for_path(&self, path: &str, pin: &str) -> Result<()> {
let sim = sim_proxy_for_modem(&self.conn, path).await?;
sim.send_pin(pin).await.map_err(|e| {
if is_wrong_pin_error(&e) {
ModemError::WrongPin
} else {
ModemError::Dbus(e)
}
})
}

pub(crate) async fn unlock_puk_for_path(
&self,
path: &str,
puk: &str,
new_pin: &str,
) -> Result<()> {
let sim = sim_proxy_for_modem(&self.conn, path).await?;
sim.send_puk(puk, new_pin).await.map_err(|e| {
if is_wrong_puk_error(&e) {
ModemError::WrongPuk
} else {
ModemError::Dbus(e)
}
})
}

pub(crate) async fn set_pin_enabled_for_path(
&self,
path: &str,
pin: &str,
enabled: bool,
) -> Result<()> {
let sim = sim_proxy_for_modem(&self.conn, path).await?;
sim.enable_pin(pin, enabled).await.map_err(|e| {
if is_wrong_pin_error(&e) {
ModemError::WrongPin
} else {
ModemError::Dbus(e)
}
})
}

pub(crate) async fn change_pin_for_path(&self, path: &str, old: &str, new: &str) -> Result<()> {
let sim = sim_proxy_for_modem(&self.conn, path).await?;
sim.change_pin(old, new).await.map_err(|e| {
if is_wrong_pin_error(&e) {
ModemError::WrongPin
} else {
ModemError::Dbus(e)
}
})
}
fn is_wrong_puk_error(error: &zbus::Error) -> bool {
let rendered = error.to_string().to_ascii_lowercase();
rendered.contains("wrong") && rendered.contains("puk")
}
.build()
.await?;

let (signal_quality, _) = proxy.signal_quality().await?;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-surface Public API design, re-exports, and semver-relevant changes dbus D-Bus types, signatures, or NM D-Bus API mmrs modemmanager bindings crate zbus zbus API surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mmrs: high-level api (ModemManager struct)

2 participants