1mod controls;
2mod monitoring;
3mod types;
4pub mod wifi;
6pub mod wired;
8
9use std::{collections::HashMap, sync::Arc};
10
11use controls::DeviceControls;
12use derive_more::Debug;
13use futures::{Stream, StreamExt};
14use tokio_util::sync::CancellationToken;
15pub use types::DeviceStateChangedEvent;
16use types::{AppliedConnection, DeviceProperties};
17pub(crate) use types::{DeviceParams, LiveDeviceParams};
18use wayle_core::{Property, unwrap_dbus, unwrap_dbus_or};
19use wayle_traits::{ModelMonitoring, Reactive};
20use zbus::{
21 Connection,
22 zvariant::{OwnedObjectPath, OwnedValue},
23};
24
25use crate::{
26 error::Error,
27 proxy::devices::DeviceProxy,
28 types::{
29 connectivity::{NMConnectivityState, NMMetered},
30 device::{LldpNeighbor, NMDeviceManaged, NMDeviceManagedFlags, NMDeviceType},
31 flags::{NMDeviceCapabilities, NMDeviceInterfaceFlags},
32 states::{NMDeviceState, NMDeviceStateReason},
33 },
34};
35
36#[derive(Debug, Clone)]
41pub struct Device {
42 #[debug(skip)]
43 pub(crate) connection: Connection,
44 #[debug(skip)]
45 pub(crate) cancellation_token: Option<CancellationToken>,
46
47 pub object_path: OwnedObjectPath,
49
50 pub udi: Property<String>,
58
59 pub udev_path: Property<String>,
61
62 pub interface: Property<String>,
66
67 pub ip_interface: Property<String>,
73
74 pub driver: Property<String>,
76
77 pub driver_version: Property<String>,
80
81 pub firmware_version: Property<String>,
83
84 pub capabilities: Property<NMDeviceCapabilities>,
86
87 pub state: Property<NMDeviceState>,
89
90 pub state_reason: Property<(NMDeviceState, NMDeviceStateReason)>,
92
93 pub active_connection: Property<OwnedObjectPath>,
98
99 pub ip4_config: Property<OwnedObjectPath>,
102
103 pub dhcp4_config: Property<OwnedObjectPath>,
106
107 pub ip6_config: Property<OwnedObjectPath>,
110
111 pub dhcp6_config: Property<OwnedObjectPath>,
114
115 pub managed: Property<bool>,
119
120 pub autoconnect: Property<bool>,
125
126 pub firmware_missing: Property<bool>,
129
130 pub nm_plugin_missing: Property<bool>,
133
134 pub device_type: Property<NMDeviceType>,
136
137 pub available_connections: Property<Vec<OwnedObjectPath>>,
140
141 pub physical_port_id: Property<String>,
145
146 pub mtu: Property<u32>,
148
149 pub metered: Property<NMMetered>,
152
153 pub lldp_neighbors: Property<Vec<LldpNeighbor>>,
156
157 pub real: Property<bool>,
161
162 pub ip4_connectivity: Property<NMConnectivityState>,
164
165 pub ip6_connectivity: Property<NMConnectivityState>,
167
168 pub interface_flags: Property<NMDeviceInterfaceFlags>,
171
172 pub hw_address: Property<String>,
174
175 pub ports: Property<Vec<OwnedObjectPath>>,
178}
179
180impl Reactive for Device {
181 type Context<'a> = DeviceParams<'a>;
182 type LiveContext<'a> = LiveDeviceParams<'a>;
183 type Error = Error;
184
185 async fn get(params: Self::Context<'_>) -> Result<Self, Self::Error> {
186 Self::from_path(params.connection, params.object_path, None).await
187 }
188
189 async fn get_live(params: Self::LiveContext<'_>) -> Result<Arc<Self>, Self::Error> {
190 let device = Self::from_path(
191 params.connection,
192 params.object_path.clone(),
193 Some(params.cancellation_token.child_token()),
194 )
195 .await
196 .map_err(|e| Error::ObjectCreationFailed {
197 object_type: String::from("Device"),
198 object_path: params.object_path.clone(),
199 source: e.into(),
200 })?;
201
202 let device = Arc::new(device);
203 device.clone().start_monitoring().await?;
204
205 Ok(device)
206 }
207}
208
209impl Device {
210 pub(crate) async fn from_path(
211 connection: &Connection,
212 object_path: OwnedObjectPath,
213 cancellation_token: Option<CancellationToken>,
214 ) -> Result<Self, Error> {
215 let proxy = DeviceProxy::new(connection, &object_path).await?;
216 let props = Self::fetch_properties(&proxy).await?;
217 Ok(Self::from_properties(
218 props,
219 connection,
220 object_path,
221 cancellation_token,
222 ))
223 }
224
225 #[allow(clippy::too_many_lines)]
226 async fn fetch_properties(proxy: &DeviceProxy<'_>) -> Result<DeviceProperties, Error> {
227 let (udi, path, interface, ip_interface, driver, driver_version, firmware_version) = tokio::join!(
228 proxy.udi(),
229 proxy.path(),
230 proxy.interface(),
231 proxy.ip_interface(),
232 proxy.driver(),
233 proxy.driver_version(),
234 proxy.firmware_version(),
235 );
236
237 let (
238 capabilities,
239 state,
240 state_reason,
241 active_connection,
242 ip4_config,
243 dhcp4_config,
244 ip6_config,
245 dhcp6_config,
246 ) = tokio::join!(
247 proxy.capabilities(),
248 proxy.state(),
249 proxy.state_reason(),
250 proxy.active_connection(),
251 proxy.ip4_config(),
252 proxy.dhcp4_config(),
253 proxy.ip6_config(),
254 proxy.dhcp6_config(),
255 );
256
257 let (
258 managed,
259 autoconnect,
260 firmware_missing,
261 nm_plugin_missing,
262 device_type,
263 available_connections,
264 physical_port_id,
265 mtu,
266 ) = tokio::join!(
267 proxy.managed(),
268 proxy.autoconnect(),
269 proxy.firmware_missing(),
270 proxy.nm_plugin_missing(),
271 proxy.device_type(),
272 proxy.available_connections(),
273 proxy.physical_port_id(),
274 proxy.mtu(),
275 );
276
277 let (
278 metered,
279 real,
280 ip4_connectivity,
281 ip6_connectivity,
282 interface_flags,
283 hw_address,
284 ports,
285 _lldp_neighbors,
286 ) = tokio::join!(
287 proxy.metered(),
288 proxy.real(),
289 proxy.ip4_connectivity(),
290 proxy.ip6_connectivity(),
291 proxy.interface_flags(),
292 proxy.hw_address(),
293 proxy.ports(),
294 proxy.lldp_neighbors(),
295 );
296
297 let device_path = path.clone().unwrap_or_default();
298
299 let available_connections: Vec<OwnedObjectPath> =
300 unwrap_dbus!(available_connections, device_path)
301 .into_iter()
302 .map(|p| OwnedObjectPath::try_from(p.to_string()).unwrap_or_default())
303 .collect();
304
305 let ports: Vec<OwnedObjectPath> = unwrap_dbus!(ports, device_path)
306 .into_iter()
307 .map(|p| OwnedObjectPath::try_from(p.to_string()).unwrap_or_default())
308 .collect();
309
310 Ok(DeviceProperties {
311 udi: unwrap_dbus!(udi, device_path),
312 interface: unwrap_dbus!(interface, device_path),
313 ip_interface: unwrap_dbus!(ip_interface, device_path),
314 driver: unwrap_dbus!(driver, device_path),
315 driver_version: unwrap_dbus!(driver_version, device_path),
316 firmware_version: unwrap_dbus!(firmware_version, device_path),
317 capabilities: unwrap_dbus!(capabilities, device_path),
318 state: unwrap_dbus!(state, device_path),
319 state_reason: state_reason.unwrap_or((0, 0)),
320 active_connection: unwrap_dbus!(active_connection, device_path),
321 ip4_config: unwrap_dbus!(ip4_config, device_path),
322 dhcp4_config: unwrap_dbus!(dhcp4_config, device_path),
323 ip6_config: unwrap_dbus!(ip6_config, device_path),
324 dhcp6_config: unwrap_dbus!(dhcp6_config, device_path),
325 managed: unwrap_dbus_or!(managed, device_path, true),
326 autoconnect: unwrap_dbus!(autoconnect, device_path),
327 firmware_missing: unwrap_dbus!(firmware_missing, device_path),
328 nm_plugin_missing: unwrap_dbus!(nm_plugin_missing, device_path),
329 device_type: unwrap_dbus!(device_type, device_path),
330 available_connections,
331 physical_port_id: unwrap_dbus!(physical_port_id, device_path),
332 mtu: unwrap_dbus_or!(mtu, device_path, 1500),
333 metered: unwrap_dbus!(metered, device_path),
334 real: unwrap_dbus_or!(real, device_path, true),
335 ip4_connectivity: unwrap_dbus!(ip4_connectivity, device_path),
336 ip6_connectivity: unwrap_dbus!(ip6_connectivity, device_path),
337 interface_flags: unwrap_dbus!(interface_flags, device_path),
338 hw_address: unwrap_dbus!(hw_address, device_path),
339 ports,
340 udev_path: device_path,
341 })
342 }
343
344 fn from_properties(
345 props: DeviceProperties,
346 connection: &Connection,
347 object_path: OwnedObjectPath,
348 cancellation_token: Option<CancellationToken>,
349 ) -> Self {
350 Self {
351 cancellation_token,
352 connection: connection.clone(),
353 object_path,
354 udi: Property::new(props.udi),
355 udev_path: Property::new(props.udev_path),
356 interface: Property::new(props.interface),
357 ip_interface: Property::new(props.ip_interface),
358 driver: Property::new(props.driver),
359 driver_version: Property::new(props.driver_version),
360 firmware_version: Property::new(props.firmware_version),
361 capabilities: Property::new(NMDeviceCapabilities::from_bits_truncate(
362 props.capabilities,
363 )),
364 state: Property::new(NMDeviceState::from_u32(props.state)),
365 state_reason: Property::new((
366 NMDeviceState::from_u32(props.state_reason.0),
367 NMDeviceStateReason::from_u32(props.state_reason.1),
368 )),
369 active_connection: Property::new(props.active_connection),
370 ip4_config: Property::new(props.ip4_config),
371 dhcp4_config: Property::new(props.dhcp4_config),
372 ip6_config: Property::new(props.ip6_config),
373 dhcp6_config: Property::new(props.dhcp6_config),
374 managed: Property::new(props.managed),
375 autoconnect: Property::new(props.autoconnect),
376 firmware_missing: Property::new(props.firmware_missing),
377 nm_plugin_missing: Property::new(props.nm_plugin_missing),
378 device_type: Property::new(NMDeviceType::from_u32(props.device_type)),
379 available_connections: Property::new(props.available_connections),
380 physical_port_id: Property::new(props.physical_port_id),
381 mtu: Property::new(props.mtu),
382 metered: Property::new(NMMetered::from_u32(props.metered)),
383 real: Property::new(props.real),
384 ip4_connectivity: Property::new(NMConnectivityState::from_u32(props.ip4_connectivity)),
385 ip6_connectivity: Property::new(NMConnectivityState::from_u32(props.ip6_connectivity)),
386 interface_flags: Property::new(NMDeviceInterfaceFlags::from_bits_truncate(
387 props.interface_flags,
388 )),
389 hw_address: Property::new(props.hw_address),
390 ports: Property::new(props.ports),
391 lldp_neighbors: Property::new(vec![]),
394 }
395 }
396
397 pub async fn set_managed(&self, managed: bool) -> Result<(), Error> {
402 DeviceControls::set_managed(&self.connection, &self.object_path, managed).await
403 }
404
405 pub async fn set_autoconnect(&self, autoconnect: bool) -> Result<(), Error> {
410 DeviceControls::set_autoconnect(&self.connection, &self.object_path, autoconnect).await
411 }
412
413 pub async fn reapply(
423 &self,
424 connection_settings: HashMap<String, HashMap<String, OwnedValue>>,
425 version_id: u64,
426 flags: u32,
427 ) -> Result<(), Error> {
428 DeviceControls::reapply(
429 &self.connection,
430 &self.object_path,
431 connection_settings,
432 version_id,
433 flags,
434 )
435 .await
436 }
437
438 pub async fn get_applied_connection(&self, flags: u32) -> Result<AppliedConnection, Error> {
450 DeviceControls::get_applied_connection(&self.connection, &self.object_path, flags).await
451 }
452
453 pub async fn disconnect(&self) -> Result<(), Error> {
458 DeviceControls::disconnect(&self.connection, &self.object_path).await
459 }
460
461 pub async fn delete(&self) -> Result<(), Error> {
466 DeviceControls::delete(&self.connection, &self.object_path).await
467 }
468
469 pub async fn set_managed_ext(
474 &self,
475 managed: NMDeviceManaged,
476 flags: NMDeviceManagedFlags,
477 ) -> Result<(), Error> {
478 DeviceControls::set_managed_ext(&self.connection, &self.object_path, managed, flags).await
479 }
480
481 pub async fn device_state_changed_signal(
486 &self,
487 ) -> Result<impl Stream<Item = DeviceStateChangedEvent>, Error> {
488 let proxy = DeviceProxy::new(&self.connection, &self.object_path).await?;
489 let stream = proxy.receive_device_state_changed().await?;
490
491 Ok(stream.filter_map(|signal| async move {
492 signal.args().ok().map(|args| DeviceStateChangedEvent {
493 new_state: NMDeviceState::from_u32(args.new_state),
494 old_state: NMDeviceState::from_u32(args.old_state),
495 reason: NMDeviceStateReason::from_u32(args.reason),
496 })
497 }))
498 }
499}