diff --git a/src/clients/networkmanager/dbus.rs b/src/clients/networkmanager/dbus.rs index 25c289a..a231dbe 100644 --- a/src/clients/networkmanager/dbus.rs +++ b/src/clients/networkmanager/dbus.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use color_eyre::Result; use zbus::dbus_proxy; use zbus::zvariant::{ObjectPath, OwnedValue, Str}; @@ -67,6 +69,12 @@ trait DeviceDbus { #[dbus_proxy(property)] fn state(&self) -> Result; + + #[dbus_proxy(property)] + fn interface(&self) -> Result; + + #[dbus_proxy(property)] + fn ip4_config(&self) -> Result; } #[dbus_proxy( @@ -89,6 +97,19 @@ trait AccessPointDbus { #[dbus_proxy(property)] fn strength(&self) -> Result; + + #[dbus_proxy(property)] + fn hw_address(&self) -> Result; +} + +// based on code generated by `zbus-xmlgen system org.freedesktop.NetworkManager /org/freedesktop/NetworkManager/AccessPoint/1` +#[dbus_proxy( + default_service = "org.freedesktop.NetworkManager", + interface = "org.freedesktop.NetworkManager.IP4Config" +)] +trait Ip4ConfigDbus { + #[dbus_proxy(property)] + fn address_data(&self) -> Result>>; } #[derive(Clone, Debug, OwnedValue, PartialEq)] diff --git a/src/clients/networkmanager/state.rs b/src/clients/networkmanager/state.rs index 8bafead..5cc1532 100644 --- a/src/clients/networkmanager/state.rs +++ b/src/clients/networkmanager/state.rs @@ -1,8 +1,8 @@ -use color_eyre::Result; +use color_eyre::{Report, Result}; use crate::clients::networkmanager::dbus::{ AccessPointDbusProxyBlocking, ActiveConnectionDbusProxyBlocking, DeviceDbusProxyBlocking, - DeviceState, DeviceType, DeviceWirelessDbusProxyBlocking, + DeviceState, DeviceType, DeviceWirelessDbusProxyBlocking, Ip4ConfigDbusProxyBlocking, }; use crate::clients::networkmanager::PathMap; @@ -33,9 +33,16 @@ pub enum WifiState { #[derive(Clone, Debug)] pub struct WifiConnectedState { + /// The SSID of the access point. pub ssid: String, + /// The MAC address of the access point. + pub bssid: String, /// Strength in percentage, from 0 to 100. pub strength: u8, + /// The IPv4 address. + pub ip4_address: String, + /// The IPv4 prefix, in bits (also known as the subnet mask length). + pub ip4_prefix: u32, } #[derive(Clone, Debug)] @@ -103,24 +110,47 @@ pub(super) fn determine_wifi_state( .build()?; let primary_access_point_path = wireless_device.active_access_point()?; if primary_access_point_path.as_str() != "/" { - connected = Some( + connected = Some(( + device, AccessPointDbusProxyBlocking::builder(dbus_connection) .path(primary_access_point_path)? .build()?, - ); + )); break; } } } } - if let Some(access_point) = connected { + if let Some((device, access_point)) = connected { let ssid = access_point .ssid() .map(|x| String::from_utf8_lossy(&x).to_string()) .unwrap_or_else(|_| "unkown".into()); + let bssid = access_point.hw_address()?.to_string(); + + let ip4config = Ip4ConfigDbusProxyBlocking::builder(dbus_connection) + .path(device.ip4_config()?.clone())? + .build()?; + let address_data = ip4config.address_data()?; + // pick the first address. not sure if there are cases when there are more than one address + // (at least for wifi). + let address = &address_data + .iter() + .next() + .ok_or_else(|| Report::msg("No address in IP4Config"))?; + let ip4_address = address + .get("address") + .ok_or_else(|| Report::msg("IP address data object must have a address"))?; + let ip4_prefix = address + .get("prefix") + .ok_or_else(|| Report::msg("IP address data object must have a prefix"))?; + Ok(WifiState::Connected(WifiConnectedState { ssid, + bssid, + ip4_address: String::try_from(ip4_address.to_owned()).unwrap_or_default(), + ip4_prefix: u32::try_from(ip4_prefix.to_owned()).unwrap_or_default(), strength: access_point.strength().unwrap_or(0), })) } else if enabled { diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 04ec6fe..e2a71ed 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -112,7 +112,8 @@ impl Module for NetworkManagerModule { match &state.wifi { WifiState::Connected(state) => { - wifi_icon.set_tooltip_text(Some(&state.ssid)); + let tooltip = format!("{}\n{}/{}", state.ssid, state.ip4_address, state.ip4_prefix); + wifi_icon.set_tooltip_text(Some(&tooltip)); }, _ => { wifi_icon.set_tooltip_text(None);