From 0c2d0cf5c1af31cd5e6f328852f11c6bfc081f69 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sat, 22 Nov 2025 21:52:14 +0100 Subject: [PATCH] Fix D-Bus enum type conversions for NetworkManager (#6325) Co-authored-by: Claude --- supervisor/dbus/const.py | 2 ++ supervisor/dbus/network/__init__.py | 7 ++++--- supervisor/dbus/network/connection.py | 2 +- supervisor/dbus/network/interface.py | 13 ++++++++++++- tests/dbus/network/test_interface.py | 17 +++++++++++++++++ 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/supervisor/dbus/const.py b/supervisor/dbus/const.py index 09b9d6b8e..54817bdaf 100644 --- a/supervisor/dbus/const.py +++ b/supervisor/dbus/const.py @@ -306,6 +306,8 @@ class DeviceType(IntEnum): VLAN = 11 TUN = 16 VETH = 20 + WIREGUARD = 29 + LOOPBACK = 32 class WirelessMethodType(IntEnum): diff --git a/supervisor/dbus/network/__init__.py b/supervisor/dbus/network/__init__.py index a5f5e18ae..0c12bfc9d 100644 --- a/supervisor/dbus/network/__init__.py +++ b/supervisor/dbus/network/__init__.py @@ -134,9 +134,10 @@ class NetworkManager(DBusInterfaceProxy): async def check_connectivity(self, *, force: bool = False) -> ConnectivityState: """Check the connectivity of the host.""" if force: - return await self.connected_dbus.call("check_connectivity") - else: - return await self.connected_dbus.get("connectivity") + return ConnectivityState( + await self.connected_dbus.call("check_connectivity") + ) + return ConnectivityState(await self.connected_dbus.get("connectivity")) async def connect(self, bus: MessageBus) -> None: """Connect to system's D-Bus.""" diff --git a/supervisor/dbus/network/connection.py b/supervisor/dbus/network/connection.py index 76340afbc..fb4422190 100644 --- a/supervisor/dbus/network/connection.py +++ b/supervisor/dbus/network/connection.py @@ -69,7 +69,7 @@ class NetworkConnection(DBusInterfaceProxy): @dbus_property def state(self) -> ConnectionStateType: """Return the state of the connection.""" - return self.properties[DBUS_ATTR_STATE] + return ConnectionStateType(self.properties[DBUS_ATTR_STATE]) @property def state_flags(self) -> set[ConnectionStateFlags]: diff --git a/supervisor/dbus/network/interface.py b/supervisor/dbus/network/interface.py index a894e405e..f7528c58a 100644 --- a/supervisor/dbus/network/interface.py +++ b/supervisor/dbus/network/interface.py @@ -1,5 +1,6 @@ """NetworkInterface object for Network Manager.""" +import logging from typing import Any from dbus_fast.aio.message_bus import MessageBus @@ -23,6 +24,8 @@ from .connection import NetworkConnection from .setting import NetworkSetting from .wireless import NetworkWireless +_LOGGER: logging.Logger = logging.getLogger(__name__) + class NetworkInterface(DBusInterfaceProxy): """NetworkInterface object represents Network Manager Device objects. @@ -57,7 +60,15 @@ class NetworkInterface(DBusInterfaceProxy): @dbus_property def type(self) -> DeviceType: """Return interface type.""" - return self.properties[DBUS_ATTR_DEVICE_TYPE] + try: + return DeviceType(self.properties[DBUS_ATTR_DEVICE_TYPE]) + except ValueError: + _LOGGER.debug( + "Unknown device type %s for %s, treating as UNKNOWN", + self.properties[DBUS_ATTR_DEVICE_TYPE], + self.object_path, + ) + return DeviceType.UNKNOWN @property @dbus_property diff --git a/tests/dbus/network/test_interface.py b/tests/dbus/network/test_interface.py index 8189ca614..ae4ad9c96 100644 --- a/tests/dbus/network/test_interface.py +++ b/tests/dbus/network/test_interface.py @@ -184,3 +184,20 @@ async def test_interface_becomes_unmanaged( assert wireless.is_connected is False assert eth0.connection is None assert connection.is_connected is False + + +async def test_unknown_device_type( + device_eth0_service: DeviceService, dbus_session_bus: MessageBus +): + """Test unknown device types are handled gracefully.""" + interface = NetworkInterface("/org/freedesktop/NetworkManager/Devices/1") + await interface.connect(dbus_session_bus) + + # Emit an unknown device type (e.g., 1000 which doesn't exist in the enum) + device_eth0_service.emit_properties_changed({"DeviceType": 1000}) + await device_eth0_service.ping() + + # Should return UNKNOWN instead of crashing + assert interface.type == DeviceType.UNKNOWN + # Wireless should be None since it's not a wireless device + assert interface.wireless is None