Skip to main content

wayle_network/core/access_point/
mod.rs

1use std::sync::Arc;
2
3use derive_more::Debug;
4use tokio_util::sync::CancellationToken;
5use wayle_core::{Property, unwrap_dbus, unwrap_dbus_or};
6use wayle_traits::{ModelMonitoring, Reactive};
7use zbus::{Connection, zvariant::OwnedObjectPath};
8
9use self::types::{AccessPointParams, Bssid, LiveAccessPointParams};
10use crate::{
11    error::Error,
12    proxy::access_point::AccessPointProxy,
13    types::{
14        flags::{NM80211ApFlags, NM80211ApSecurityFlags},
15        wifi::NM80211Mode,
16    },
17};
18
19pub(crate) mod monitoring;
20pub(crate) mod types;
21
22pub use self::types::{SecurityType, Ssid};
23
24/// WiFi access point representation.
25///
26/// Provides information about a detected WiFi access point including its
27/// security configuration, signal strength, frequency, and identification.
28/// Access points are discovered and monitored through the WiFi device interface.
29#[derive(Debug, Clone)]
30pub struct AccessPoint {
31    #[debug(skip)]
32    pub(crate) connection: Connection,
33    #[debug(skip)]
34    pub(crate) object_path: OwnedObjectPath,
35    #[debug(skip)]
36    pub(crate) cancellation_token: Option<CancellationToken>,
37    /// Flags describing the capabilities of the access point. See NM80211ApFlags.
38    pub flags: Property<NM80211ApFlags>,
39
40    /// Flags describing the access point's capabilities according to WPA (Wifi Protected Access).
41    /// See NM80211ApSecurityFlags.
42    pub wpa_flags: Property<NM80211ApSecurityFlags>,
43
44    /// Flags describing the access point's capabilities according to the
45    /// RSN (Robust Secure Network) protocol. See NM80211ApSecurityFlags.
46    pub rsn_flags: Property<NM80211ApSecurityFlags>,
47
48    /// The Service Set Identifier identifying the access point.
49    /// The Ssid is a binary array to support non-UTF-8 Ssids.
50    pub ssid: Property<Ssid>,
51
52    /// The radio channel frequency in use by the access point, in MHz.
53    pub frequency: Property<u32>,
54
55    /// The hardware address (Bssid) of the access point.
56    pub bssid: Property<Bssid>,
57
58    /// Describes the operating mode of the access point.
59    pub mode: Property<NM80211Mode>,
60
61    /// The maximum bitrate this access point is capable of, in kilobits/second (Kb/s).
62    pub max_bitrate: Property<u32>,
63
64    /// The bandwidth announced by the access point in MHz.
65    pub bandwidth: Property<u32>,
66
67    /// The current signal quality of the access point, in percent.
68    pub strength: Property<u8>,
69
70    /// The timestamp (in CLOCK_BOOTTIME seconds) for the last time the access point
71    /// was found in scan results. A value of -1 means the access point has never
72    /// been found in scan results.
73    pub last_seen: Property<i32>,
74
75    /// Simplified security type derived from flags.
76    ///
77    /// Provides a user-friendly classification of the AP's security.
78    pub security: Property<SecurityType>,
79
80    /// Whether this is a hidden network (non-broadcasting Ssid).
81    pub is_hidden: Property<bool>,
82}
83
84impl Reactive for AccessPoint {
85    type Context<'a> = AccessPointParams<'a>;
86    type LiveContext<'a> = LiveAccessPointParams<'a>;
87    type Error = Error;
88
89    async fn get(params: Self::Context<'_>) -> Result<Self, Self::Error> {
90        let ap = Self::from_path(params.connection, params.path.clone(), None)
91            .await
92            .map_err(|e| match e {
93                Error::ObjectNotFound(_) => e,
94                _ => Error::ObjectCreationFailed {
95                    object_type: String::from("AccessPoint"),
96                    object_path: params.path.clone(),
97                    source: e.into(),
98                },
99            })?;
100
101        Ok(ap)
102    }
103
104    async fn get_live(params: Self::LiveContext<'_>) -> Result<Arc<Self>, Self::Error> {
105        let access_point = Self::from_path(
106            params.connection,
107            params.path.clone(),
108            Some(params.cancellation_token.child_token()),
109        )
110        .await
111        .map_err(|e| match e {
112            Error::ObjectNotFound(_) => e,
113            _ => Error::ObjectCreationFailed {
114                object_type: String::from("AccessPoint"),
115                object_path: params.path.clone(),
116                source: e.into(),
117            },
118        })?;
119        let access_point = Arc::new(access_point);
120        access_point.clone().start_monitoring().await?;
121
122        Ok(access_point)
123    }
124}
125
126impl PartialEq for AccessPoint {
127    fn eq(&self, other: &Self) -> bool {
128        self.bssid.get() == other.bssid.get()
129    }
130}
131
132impl AccessPoint {
133    /// Returns the D-Bus object path for this access point.
134    pub fn object_path(&self) -> &OwnedObjectPath {
135        &self.object_path
136    }
137
138    async fn from_path(
139        connection: &Connection,
140        path: OwnedObjectPath,
141        cancellation_token: Option<CancellationToken>,
142    ) -> Result<Self, Error> {
143        let ap_proxy = AccessPointProxy::new(connection, &path)
144            .await
145            .map_err(Error::DbusError)?;
146
147        if ap_proxy.strength().await.is_err() {
148            return Err(Error::ObjectNotFound(path.clone()));
149        }
150
151        let (
152            flags,
153            wpa_flags,
154            rsn_flags,
155            ssid,
156            frequency,
157            hw_address,
158            mode,
159            max_bitrate,
160            bandwidth,
161            strength,
162            last_seen,
163        ) = tokio::join!(
164            ap_proxy.flags(),
165            ap_proxy.wpa_flags(),
166            ap_proxy.rsn_flags(),
167            ap_proxy.ssid(),
168            ap_proxy.frequency(),
169            ap_proxy.hw_address(),
170            ap_proxy.mode(),
171            ap_proxy.max_bitrate(),
172            ap_proxy.bandwidth(),
173            ap_proxy.strength(),
174            ap_proxy.last_seen(),
175        );
176
177        let flags = NM80211ApFlags::from_bits_truncate(unwrap_dbus!(flags, path));
178        let wpa_flags = NM80211ApSecurityFlags::from_bits_truncate(unwrap_dbus!(wpa_flags, path));
179        let rsn_flags = NM80211ApSecurityFlags::from_bits_truncate(unwrap_dbus!(rsn_flags, path));
180        let ssid = Ssid::new(unwrap_dbus!(ssid, path));
181        let frequency = unwrap_dbus!(frequency, path);
182        let hw_address = Bssid::new(unwrap_dbus!(hw_address, path).into_bytes());
183        let mode = NM80211Mode::from_u32(unwrap_dbus!(mode, path));
184        let max_bitrate = unwrap_dbus!(max_bitrate, path);
185        let bandwidth = unwrap_dbus!(bandwidth, path);
186        let strength = unwrap_dbus!(strength, path);
187        let last_seen = unwrap_dbus_or!(last_seen, path, -1);
188
189        let security = SecurityType::from_flags(flags, wpa_flags, rsn_flags);
190        let is_hidden = ssid.is_empty();
191
192        Ok(Self {
193            connection: connection.clone(),
194            object_path: path.clone(),
195            cancellation_token,
196            flags: Property::new(flags),
197            wpa_flags: Property::new(wpa_flags),
198            rsn_flags: Property::new(rsn_flags),
199            ssid: Property::new(ssid),
200            frequency: Property::new(frequency),
201            bssid: Property::new(hw_address),
202            mode: Property::new(mode),
203            max_bitrate: Property::new(max_bitrate),
204            bandwidth: Property::new(bandwidth),
205            strength: Property::new(strength),
206            last_seen: Property::new(last_seen),
207            security: Property::new(security),
208            is_hidden: Property::new(is_hidden),
209        })
210    }
211}