Velbus add subdevices for din-rail modules (#131371)
parent
4ddb72314d
commit
eaaab4ccfe
|
@ -14,6 +14,12 @@ from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
# device identifiers for modules
|
||||||
|
# (DOMAIN, module_address)
|
||||||
|
|
||||||
|
# device identifiers for channels that are subdevices of a module
|
||||||
|
# (DOMAIN, f"{module_address}-{channel_number}")
|
||||||
|
|
||||||
|
|
||||||
class VelbusEntity(Entity):
|
class VelbusEntity(Entity):
|
||||||
"""Representation of a Velbus entity."""
|
"""Representation of a Velbus entity."""
|
||||||
|
@ -23,19 +29,33 @@ class VelbusEntity(Entity):
|
||||||
def __init__(self, channel: VelbusChannel) -> None:
|
def __init__(self, channel: VelbusChannel) -> None:
|
||||||
"""Initialize a Velbus entity."""
|
"""Initialize a Velbus entity."""
|
||||||
self._channel = channel
|
self._channel = channel
|
||||||
|
self._module_adress = str(channel.get_module_address())
|
||||||
self._attr_name = channel.get_name()
|
self._attr_name = channel.get_name()
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={
|
identifiers={
|
||||||
(DOMAIN, str(channel.get_module_address())),
|
(DOMAIN, self._get_identifier()),
|
||||||
},
|
},
|
||||||
manufacturer="Velleman",
|
manufacturer="Velleman",
|
||||||
model=channel.get_module_type_name(),
|
model=channel.get_module_type_name(),
|
||||||
|
model_id=str(channel.get_module_type()),
|
||||||
name=channel.get_full_name(),
|
name=channel.get_full_name(),
|
||||||
sw_version=channel.get_module_sw_version(),
|
sw_version=channel.get_module_sw_version(),
|
||||||
|
serial_number=channel.get_module_serial(),
|
||||||
)
|
)
|
||||||
serial = channel.get_module_serial() or str(channel.get_module_address())
|
if self._channel.is_sub_device():
|
||||||
|
self._attr_device_info["via_device"] = (
|
||||||
|
DOMAIN,
|
||||||
|
self._module_adress,
|
||||||
|
)
|
||||||
|
serial = channel.get_module_serial() or self._module_adress
|
||||||
self._attr_unique_id = f"{serial}-{channel.get_channel_number()}"
|
self._attr_unique_id = f"{serial}-{channel.get_channel_number()}"
|
||||||
|
|
||||||
|
def _get_identifier(self) -> str:
|
||||||
|
"""Return the identifier of the entity."""
|
||||||
|
if not self._channel.is_sub_device():
|
||||||
|
return self._module_adress
|
||||||
|
return f"{self._module_adress}-{self._channel.get_channel_number()}"
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Add listener for state changes."""
|
"""Add listener for state changes."""
|
||||||
self._channel.on_status_update(self._on_update)
|
self._channel.on_status_update(self._on_update)
|
||||||
|
|
|
@ -113,9 +113,11 @@ def mock_button() -> AsyncMock:
|
||||||
channel.get_module_address.return_value = 1
|
channel.get_module_address.return_value = 1
|
||||||
channel.get_channel_number.return_value = 1
|
channel.get_channel_number.return_value = 1
|
||||||
channel.get_module_type_name.return_value = "VMB4RYLD"
|
channel.get_module_type_name.return_value = "VMB4RYLD"
|
||||||
|
channel.get_module_type.return_value = 99
|
||||||
channel.get_full_name.return_value = "Channel full name"
|
channel.get_full_name.return_value = "Channel full name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.0"
|
channel.get_module_sw_version.return_value = "1.0.0"
|
||||||
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
||||||
|
channel.is_sub_device.return_value = False
|
||||||
channel.is_closed.return_value = True
|
channel.is_closed.return_value = True
|
||||||
channel.is_on.return_value = False
|
channel.is_on.return_value = False
|
||||||
return channel
|
return channel
|
||||||
|
@ -133,6 +135,8 @@ def mock_temperature() -> AsyncMock:
|
||||||
channel.get_full_name.return_value = "Channel full name"
|
channel.get_full_name.return_value = "Channel full name"
|
||||||
channel.get_module_sw_version.return_value = "3.0.0"
|
channel.get_module_sw_version.return_value = "3.0.0"
|
||||||
channel.get_module_serial.return_value = "asdfghjk"
|
channel.get_module_serial.return_value = "asdfghjk"
|
||||||
|
channel.get_module_type.return_value = 1
|
||||||
|
channel.is_sub_device.return_value = False
|
||||||
channel.is_counter_channel.return_value = False
|
channel.is_counter_channel.return_value = False
|
||||||
channel.get_class.return_value = "temperature"
|
channel.get_class.return_value = "temperature"
|
||||||
channel.get_unit.return_value = "°C"
|
channel.get_unit.return_value = "°C"
|
||||||
|
@ -153,12 +157,14 @@ def mock_relay() -> AsyncMock:
|
||||||
channel = AsyncMock(spec=Relay)
|
channel = AsyncMock(spec=Relay)
|
||||||
channel.get_categories.return_value = ["switch"]
|
channel.get_categories.return_value = ["switch"]
|
||||||
channel.get_name.return_value = "RelayName"
|
channel.get_name.return_value = "RelayName"
|
||||||
channel.get_module_address.return_value = 99
|
channel.get_module_address.return_value = 88
|
||||||
channel.get_channel_number.return_value = 55
|
channel.get_channel_number.return_value = 55
|
||||||
channel.get_module_type_name.return_value = "VMB4RYNO"
|
channel.get_module_type_name.return_value = "VMB4RYNO"
|
||||||
channel.get_full_name.return_value = "Full relay name"
|
channel.get_full_name.return_value = "Full relay name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.1"
|
channel.get_module_sw_version.return_value = "1.0.1"
|
||||||
channel.get_module_serial.return_value = "qwerty123"
|
channel.get_module_serial.return_value = "qwerty123"
|
||||||
|
channel.get_module_type.return_value = 2
|
||||||
|
channel.is_sub_device.return_value = True
|
||||||
channel.is_on.return_value = True
|
channel.is_on.return_value = True
|
||||||
return channel
|
return channel
|
||||||
|
|
||||||
|
@ -169,12 +175,14 @@ def mock_select() -> AsyncMock:
|
||||||
channel = AsyncMock(spec=SelectedProgram)
|
channel = AsyncMock(spec=SelectedProgram)
|
||||||
channel.get_categories.return_value = ["select"]
|
channel.get_categories.return_value = ["select"]
|
||||||
channel.get_name.return_value = "select"
|
channel.get_name.return_value = "select"
|
||||||
channel.get_module_address.return_value = 55
|
channel.get_module_address.return_value = 88
|
||||||
channel.get_channel_number.return_value = 33
|
channel.get_channel_number.return_value = 33
|
||||||
channel.get_module_type_name.return_value = "VMB4RYNO"
|
channel.get_module_type_name.return_value = "VMB4RYNO"
|
||||||
|
channel.get_module_type.return_value = 3
|
||||||
channel.get_full_name.return_value = "Full module name"
|
channel.get_full_name.return_value = "Full module name"
|
||||||
channel.get_module_sw_version.return_value = "1.1.1"
|
channel.get_module_sw_version.return_value = "1.1.1"
|
||||||
channel.get_module_serial.return_value = "qwerty1234567"
|
channel.get_module_serial.return_value = "qwerty1234567"
|
||||||
|
channel.is_sub_device.return_value = False
|
||||||
channel.get_options.return_value = ["none", "summer", "winter", "holiday"]
|
channel.get_options.return_value = ["none", "summer", "winter", "holiday"]
|
||||||
channel.get_selected_program.return_value = "winter"
|
channel.get_selected_program.return_value = "winter"
|
||||||
return channel
|
return channel
|
||||||
|
@ -186,12 +194,14 @@ def mock_buttoncounter() -> AsyncMock:
|
||||||
channel = AsyncMock(spec=ButtonCounter)
|
channel = AsyncMock(spec=ButtonCounter)
|
||||||
channel.get_categories.return_value = ["sensor"]
|
channel.get_categories.return_value = ["sensor"]
|
||||||
channel.get_name.return_value = "ButtonCounter"
|
channel.get_name.return_value = "ButtonCounter"
|
||||||
channel.get_module_address.return_value = 2
|
channel.get_module_address.return_value = 88
|
||||||
channel.get_channel_number.return_value = 2
|
channel.get_channel_number.return_value = 2
|
||||||
channel.get_module_type_name.return_value = "VMB7IN"
|
channel.get_module_type_name.return_value = "VMB7IN"
|
||||||
|
channel.get_module_type.return_value = 4
|
||||||
channel.get_full_name.return_value = "Channel full name"
|
channel.get_full_name.return_value = "Channel full name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.0"
|
channel.get_module_sw_version.return_value = "1.0.0"
|
||||||
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
||||||
|
channel.is_sub_device.return_value = True
|
||||||
channel.is_counter_channel.return_value = True
|
channel.is_counter_channel.return_value = True
|
||||||
channel.is_temperature.return_value = False
|
channel.is_temperature.return_value = False
|
||||||
channel.get_state.return_value = 100
|
channel.get_state.return_value = 100
|
||||||
|
@ -210,9 +220,11 @@ def mock_sensornumber() -> AsyncMock:
|
||||||
channel.get_module_address.return_value = 2
|
channel.get_module_address.return_value = 2
|
||||||
channel.get_channel_number.return_value = 3
|
channel.get_channel_number.return_value = 3
|
||||||
channel.get_module_type_name.return_value = "VMB7IN"
|
channel.get_module_type_name.return_value = "VMB7IN"
|
||||||
|
channel.get_module_type.return_value = 8
|
||||||
channel.get_full_name.return_value = "Channel full name"
|
channel.get_full_name.return_value = "Channel full name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.0"
|
channel.get_module_sw_version.return_value = "1.0.0"
|
||||||
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
||||||
|
channel.is_sub_device.return_value = False
|
||||||
channel.is_counter_channel.return_value = False
|
channel.is_counter_channel.return_value = False
|
||||||
channel.is_temperature.return_value = False
|
channel.is_temperature.return_value = False
|
||||||
channel.get_unit.return_value = "m"
|
channel.get_unit.return_value = "m"
|
||||||
|
@ -229,9 +241,11 @@ def mock_lightsensor() -> AsyncMock:
|
||||||
channel.get_module_address.return_value = 2
|
channel.get_module_address.return_value = 2
|
||||||
channel.get_channel_number.return_value = 4
|
channel.get_channel_number.return_value = 4
|
||||||
channel.get_module_type_name.return_value = "VMB7IN"
|
channel.get_module_type_name.return_value = "VMB7IN"
|
||||||
|
channel.get_module_type.return_value = 8
|
||||||
channel.get_full_name.return_value = "Channel full name"
|
channel.get_full_name.return_value = "Channel full name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.0"
|
channel.get_module_sw_version.return_value = "1.0.0"
|
||||||
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
channel.get_module_serial.return_value = "a1b2c3d4e5f6"
|
||||||
|
channel.is_sub_device.return_value = False
|
||||||
channel.is_counter_channel.return_value = False
|
channel.is_counter_channel.return_value = False
|
||||||
channel.is_temperature.return_value = False
|
channel.is_temperature.return_value = False
|
||||||
channel.get_unit.return_value = "illuminance"
|
channel.get_unit.return_value = "illuminance"
|
||||||
|
@ -245,12 +259,14 @@ def mock_dimmer() -> AsyncMock:
|
||||||
channel = AsyncMock(spec=Dimmer)
|
channel = AsyncMock(spec=Dimmer)
|
||||||
channel.get_categories.return_value = ["light"]
|
channel.get_categories.return_value = ["light"]
|
||||||
channel.get_name.return_value = "Dimmer"
|
channel.get_name.return_value = "Dimmer"
|
||||||
channel.get_module_address.return_value = 3
|
channel.get_module_address.return_value = 88
|
||||||
channel.get_channel_number.return_value = 1
|
channel.get_channel_number.return_value = 10
|
||||||
channel.get_module_type_name.return_value = "VMBDN1"
|
channel.get_module_type_name.return_value = "VMBDN1"
|
||||||
|
channel.get_module_type.return_value = 9
|
||||||
channel.get_full_name.return_value = "Dimmer full name"
|
channel.get_full_name.return_value = "Dimmer full name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.0"
|
channel.get_module_sw_version.return_value = "1.0.0"
|
||||||
channel.get_module_serial.return_value = "a1b2c3d4e5f6g7"
|
channel.get_module_serial.return_value = "a1b2c3d4e5f6g7"
|
||||||
|
channel.is_sub_device.return_value = True
|
||||||
channel.is_on.return_value = False
|
channel.is_on.return_value = False
|
||||||
channel.get_dimmer_state.return_value = 33
|
channel.get_dimmer_state.return_value = 33
|
||||||
return channel
|
return channel
|
||||||
|
@ -262,12 +278,14 @@ def mock_cover() -> AsyncMock:
|
||||||
channel = AsyncMock(spec=Blind)
|
channel = AsyncMock(spec=Blind)
|
||||||
channel.get_categories.return_value = ["cover"]
|
channel.get_categories.return_value = ["cover"]
|
||||||
channel.get_name.return_value = "CoverName"
|
channel.get_name.return_value = "CoverName"
|
||||||
channel.get_module_address.return_value = 201
|
channel.get_module_address.return_value = 88
|
||||||
channel.get_channel_number.return_value = 2
|
channel.get_channel_number.return_value = 9
|
||||||
channel.get_module_type_name.return_value = "VMB2BLE"
|
channel.get_module_type_name.return_value = "VMB2BLE"
|
||||||
|
channel.get_module_type.return_value = 10
|
||||||
channel.get_full_name.return_value = "Full cover name"
|
channel.get_full_name.return_value = "Full cover name"
|
||||||
channel.get_module_sw_version.return_value = "1.0.1"
|
channel.get_module_sw_version.return_value = "1.0.1"
|
||||||
channel.get_module_serial.return_value = "1234"
|
channel.get_module_serial.return_value = "1234"
|
||||||
|
channel.is_sub_device.return_value = True
|
||||||
channel.support_position.return_value = True
|
channel.support_position.return_value = True
|
||||||
channel.get_position.return_value = 50
|
channel.get_position.return_value = 50
|
||||||
channel.is_closed.return_value = False
|
channel.is_closed.return_value = False
|
||||||
|
@ -283,12 +301,14 @@ def mock_cover_no_position() -> AsyncMock:
|
||||||
channel = AsyncMock(spec=Blind)
|
channel = AsyncMock(spec=Blind)
|
||||||
channel.get_categories.return_value = ["cover"]
|
channel.get_categories.return_value = ["cover"]
|
||||||
channel.get_name.return_value = "CoverNameNoPos"
|
channel.get_name.return_value = "CoverNameNoPos"
|
||||||
channel.get_module_address.return_value = 200
|
channel.get_module_address.return_value = 88
|
||||||
channel.get_channel_number.return_value = 1
|
channel.get_channel_number.return_value = 11
|
||||||
channel.get_module_type_name.return_value = "VMB2BLE"
|
channel.get_module_type_name.return_value = "VMB2BLE"
|
||||||
|
channel.get_module_type.return_value = 10
|
||||||
channel.get_full_name.return_value = "Full cover name no position"
|
channel.get_full_name.return_value = "Full cover name no position"
|
||||||
channel.get_module_sw_version.return_value = "1.0.1"
|
channel.get_module_sw_version.return_value = "1.0.1"
|
||||||
channel.get_module_serial.return_value = "12345"
|
channel.get_module_serial.return_value = "12345"
|
||||||
|
channel.is_sub_device.return_value = True
|
||||||
channel.support_position.return_value = False
|
channel.support_position.return_value = False
|
||||||
channel.get_position.return_value = None
|
channel.get_position.return_value = None
|
||||||
channel.is_closed.return_value = False
|
channel.is_closed.return_value = False
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <CoverEntityFeature: 15>,
|
'supported_features': <CoverEntityFeature: 15>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '1234-2',
|
'unique_id': '1234-9',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <CoverEntityFeature: 11>,
|
'supported_features': <CoverEntityFeature: 11>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': '12345-1',
|
'unique_id': '12345-11',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_device_registry
|
||||||
|
list([
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'1',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB4RYLD',
|
||||||
|
'model_id': '99',
|
||||||
|
'name': 'Channel full name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': 'a1b2c3d4e5f6',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.0',
|
||||||
|
'via_device_id': None,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'88-9',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB2BLE',
|
||||||
|
'model_id': '10',
|
||||||
|
'name': 'Full cover name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': '1234',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.1',
|
||||||
|
'via_device_id': <ANY>,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'88-11',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB2BLE',
|
||||||
|
'model_id': '10',
|
||||||
|
'name': 'Full cover name no position',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': '12345',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.1',
|
||||||
|
'via_device_id': <ANY>,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'88-10',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMBDN1',
|
||||||
|
'model_id': '9',
|
||||||
|
'name': 'Dimmer full name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': 'a1b2c3d4e5f6g7',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.0',
|
||||||
|
'via_device_id': <ANY>,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'88-2',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB7IN',
|
||||||
|
'model_id': '4',
|
||||||
|
'name': 'Channel full name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': 'a1b2c3d4e5f6',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.0',
|
||||||
|
'via_device_id': <ANY>,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'88',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB4GPO',
|
||||||
|
'model_id': '1',
|
||||||
|
'name': 'Channel full name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': 'asdfghjk',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '3.0.0',
|
||||||
|
'via_device_id': None,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'2',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB7IN',
|
||||||
|
'model_id': '8',
|
||||||
|
'name': 'Channel full name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': 'a1b2c3d4e5f6',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.0',
|
||||||
|
'via_device_id': None,
|
||||||
|
}),
|
||||||
|
DeviceRegistryEntrySnapshot({
|
||||||
|
'area_id': None,
|
||||||
|
'config_entries': <ANY>,
|
||||||
|
'configuration_url': None,
|
||||||
|
'connections': set({
|
||||||
|
}),
|
||||||
|
'disabled_by': None,
|
||||||
|
'entry_type': None,
|
||||||
|
'hw_version': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'identifiers': set({
|
||||||
|
tuple(
|
||||||
|
'velbus',
|
||||||
|
'88-55',
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'is_new': False,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'manufacturer': 'Velleman',
|
||||||
|
'model': 'VMB4RYNO',
|
||||||
|
'model_id': '2',
|
||||||
|
'name': 'Full relay name',
|
||||||
|
'name_by_user': None,
|
||||||
|
'primary_config_entry': <ANY>,
|
||||||
|
'serial_number': 'qwerty123',
|
||||||
|
'suggested_area': None,
|
||||||
|
'sw_version': '1.0.1',
|
||||||
|
'via_device_id': <ANY>,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
# ---
|
|
@ -32,7 +32,7 @@
|
||||||
'previous_unique_id': None,
|
'previous_unique_id': None,
|
||||||
'supported_features': <LightEntityFeature: 32>,
|
'supported_features': <LightEntityFeature: 32>,
|
||||||
'translation_key': None,
|
'translation_key': None,
|
||||||
'unique_id': 'a1b2c3d4e5f6g7-1',
|
'unique_id': 'a1b2c3d4e5f6g7-10',
|
||||||
'unit_of_measurement': None,
|
'unit_of_measurement': None,
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
"""Tests for the Velbus component initialisation."""
|
"""Tests for the Velbus component initialisation."""
|
||||||
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import AsyncMock, MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
from velbusaio.exceptions import VelbusConnectionFailed
|
from velbusaio.exceptions import VelbusConnectionFailed
|
||||||
|
|
||||||
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
from homeassistant.components.velbus import VelbusConfigEntry
|
from homeassistant.components.velbus import VelbusConfigEntry
|
||||||
from homeassistant.components.velbus.const import DOMAIN
|
from homeassistant.components.velbus.const import DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||||
from homeassistant.const import CONF_NAME, CONF_PORT
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_NAME, CONF_PORT, SERVICE_TURN_ON
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
|
|
||||||
from . import init_integration
|
from . import init_integration
|
||||||
|
@ -113,3 +117,46 @@ async def test_migrate_config_entry(
|
||||||
await hass.config_entries.async_setup(entry.entry_id)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
assert dict(entry.data) == legacy_config
|
assert dict(entry.data) == legacy_config
|
||||||
assert entry.version == 2
|
assert entry.version == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_api_call(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_relay: AsyncMock,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test the api call decorator action."""
|
||||||
|
await init_integration(hass, config_entry)
|
||||||
|
|
||||||
|
mock_relay.turn_on.side_effect = OSError()
|
||||||
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: "switch.relayname"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_registry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: MockConfigEntry,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test the velbus device registry."""
|
||||||
|
await init_integration(hass, config_entry)
|
||||||
|
|
||||||
|
# Ensure devices are correctly registered
|
||||||
|
device_entries = dr.async_entries_for_config_entry(
|
||||||
|
device_registry, config_entry.entry_id
|
||||||
|
)
|
||||||
|
assert device_entries == snapshot
|
||||||
|
|
||||||
|
device_parent = device_registry.async_get_device(identifiers={(DOMAIN, "88")})
|
||||||
|
assert device_parent.via_device_id is None
|
||||||
|
|
||||||
|
device = device_registry.async_get_device(identifiers={(DOMAIN, "88-9")})
|
||||||
|
assert device.via_device_id == device_parent.id
|
||||||
|
|
||||||
|
device_no_sub = device_registry.async_get_device(identifiers={(DOMAIN, "2")})
|
||||||
|
assert device_no_sub.via_device_id is None
|
||||||
|
|
Loading…
Reference in New Issue