Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change virtual device detection #151

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages = ["src/abbfreeathome"]

[project]
name = "local-abbfreeathome"
version = "1.17.4"
version = "1.18.0"
authors = [
{ name="Adam Kingsley", email="[email protected]" },
]
Expand Down
1 change: 0 additions & 1 deletion src/abbfreeathome/bin/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ class Interface(enum.Enum):
WIRELESS_RF = "RF"
HUE = "hue"
SONOS = "sonos"
VIRTUAL_DEVICE = "vdev:[email protected]"
5 changes: 5 additions & 0 deletions src/abbfreeathome/devices/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ def room_name(self) -> str | None:
"""Get the room name of the device."""
return self._room_name

@property
def is_virtual(self) -> bool | None:
"""Get the virtual-status of the device."""
return self.device_id[0:4] == "6000"

def get_input_by_pairing(self, pairing: Pairing) -> tuple[str, Any]:
"""Get the channel input by pairing id."""
for _input_id, _input in self._inputs.items():
Expand Down
13 changes: 13 additions & 0 deletions src/abbfreeathome/freeathome.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(
interfaces: list[Interface] | None = None,
device_classes: list[Base] | None = None,
include_orphan_channels: bool = False,
include_virtual_devices: bool = False,
) -> None:
"""Initialize the FreeAtHome class."""
self._config: dict | None = None
Expand All @@ -26,6 +27,7 @@ def __init__(
self._interfaces: list[Interface] = interfaces
self._device_classes: list[Base] = device_classes
self._include_orphan_channels: bool = include_orphan_channels
self._include_virtual_devices: bool = include_virtual_devices

def clear_devices(self):
"""Clear all devices in the device list."""
Expand Down Expand Up @@ -54,6 +56,16 @@ async def get_devices_by_function(self, function: Function) -> list[dict]:
"""Get the list of devices by function."""
_devices = []
for _device_key, _device in (await self.get_config()).get("devices").items():
_is_virtual = False
if _device_key[0:4] == "6000":
_is_virtual = True

if "interface" in _device:
del _device["interface"]

if not self._include_virtual_devices:
continue

# Filter by interface if provided
if self._interfaces and _device.get("interface") not in [
interface.value for interface in self._interfaces
Expand Down Expand Up @@ -84,6 +96,7 @@ async def get_devices_by_function(self, function: Function) -> list[dict]:
"channel_id": _channel_key,
"channel_name": _channel_name,
"function_id": int(_channel.get("functionID"), 16),
"is_virtual": _is_virtual,
"floor_name": await self.get_floor_name(
floor_serial_id=_channel.get(
"floor", _device.get("floor")
Expand Down
108 changes: 106 additions & 2 deletions tests/test_freeathome.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,60 @@ def api_mock():
},
},
},
"60002AE2F1BE": {
"nativeId": "abcd12350",
"deviceId": "0004",
"displayName": "MyVirtualDoorSensor",
"unresponsive": False,
"unresponsiveCounter": 0,
"defect": False,
"channels": {
"ch0000": {
"displayName": "MyVirtualDoorSensor",
"floor": "01",
"room": "01",
"functionID": "f",
"inputs": {},
"outputs": {"odp000c": {"pairingID": 53, "value": ""}},
"parameters": {"par0010": "1"},
"selectedIcon": "51",
}
},
"parameters": {},
},
"60005D808C54": {
"floor": "01",
"room": "01",
"nativeId": "virtual-switch-sleep",
"interface": "vdev:[email protected]",
"deviceId": "0001",
"displayName": "Sleepmode",
"unresponsive": False,
"unresponsiveCounter": 0,
"defect": False,
"channels": {
"ch0000": {
"displayName": "Sleepmode",
"floor": "01",
"room": "01",
"functionID": "7",
"inputs": {
"idp0000": {"pairingID": 1, "value": "1"},
"idp0001": {"pairingID": 2, "value": ""},
"idp0002": {"pairingID": 3, "value": ""},
"idp0003": {"pairingID": 4, "value": ""},
"idp0004": {"pairingID": 6, "value": ""},
},
"outputs": {
"odp0000": {"pairingID": 256, "value": "0"},
"odp0001": {"pairingID": 257, "value": "0"},
},
"parameters": {"par0015": "60", "par0014": "1"},
"selectedIcon": "0B",
}
},
"parameters": {},
},
},
}
return api
Expand All @@ -243,15 +297,33 @@ def api_mock():
def freeathome(api_mock):
"""Create the FreeAtHome fixture."""
return FreeAtHome(
api=api_mock, interfaces=[Interface.WIRED_BUS], include_orphan_channels=False
api=api_mock,
interfaces=[Interface.WIRED_BUS],
include_orphan_channels=False,
include_virtual_devices=False,
)


@pytest.fixture
def freeathome_orphans(api_mock):
"""Create the FreeAtHome fixture."""
return FreeAtHome(
api=api_mock, interfaces=[Interface.WIRED_BUS], include_orphan_channels=True
api=api_mock,
interfaces=[Interface.WIRED_BUS],
include_orphan_channels=True,
include_virtual_devices=False,
)


# This can be removed, when ABB fixes the bug
@pytest.fixture
def freeathome_virtuals(api_mock):
"""Create the FreeAtHome fixture."""
return FreeAtHome(
api=api_mock,
interfaces=[Interface.WIRED_BUS, Interface.UNDEFINED],
include_orphan_channels=False,
include_virtual_devices=True,
)


Expand Down Expand Up @@ -375,6 +447,38 @@ async def test_load_devices_with_orphans(freeathome_orphans):
assert devices[device_key].room_name is None


@pytest.mark.asyncio
async def test_load_devices_with_virtuals(freeathome_virtuals):
"""Test the load_devices function."""
await freeathome_virtuals.load_devices()

# Get the dict of devices
devices = freeathome_virtuals.get_devices()

# Verify that the devices are loaded correctly
assert len(devices) == 7

# Check a single virtual device
device_key = "60005D808C54/ch0000"
assert device_key in devices
assert isinstance(devices[device_key], SwitchActuator)
assert devices[device_key].device_name == "Sleepmode"
assert devices[device_key].channel_name == "Sleepmode"
assert devices[device_key].floor_name == "Ground Floor"
assert devices[device_key].room_name == "Living Room"
assert devices[device_key].is_virtual is True

# Check a single device
device_key = "ABB7F500E17A/ch0003"
assert device_key in devices
assert isinstance(devices[device_key], SwitchActuator)
assert devices[device_key].device_name == "Study Area Rocker"
assert devices[device_key].channel_name == "Study Area Light"
assert devices[device_key].floor_name == "Ground Floor"
assert devices[device_key].room_name == "Living Room"
assert devices[device_key].is_virtual is False


@pytest.mark.asyncio
async def test_ws_close(freeathome, api_mock):
"Test the ws_close function."
Expand Down
Loading