mirror of
https://github.com/home-assistant/core.git
synced 2025-09-06 13:21:33 +02:00
Fix Z-Wave duplicate provisioned device (#150008)
This commit is contained in:
@@ -509,7 +509,7 @@ class ControllerEvents:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.async_check_preprovisioned_device(node)
|
await self.async_check_pre_provisioned_device(node)
|
||||||
|
|
||||||
if node.is_controller_node:
|
if node.is_controller_node:
|
||||||
# Create a controller status sensor for each device
|
# Create a controller status sensor for each device
|
||||||
@@ -637,8 +637,8 @@ class ControllerEvents:
|
|||||||
f"{DOMAIN}.identify_controller.{dev_id[1]}",
|
f"{DOMAIN}.identify_controller.{dev_id[1]}",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_check_preprovisioned_device(self, node: ZwaveNode) -> None:
|
async def async_check_pre_provisioned_device(self, node: ZwaveNode) -> None:
|
||||||
"""Check if the node was preprovisioned and update the device registry."""
|
"""Check if the node was pre-provisioned and update the device registry."""
|
||||||
provisioning_entry = (
|
provisioning_entry = (
|
||||||
await self.driver_events.driver.controller.async_get_provisioning_entry(
|
await self.driver_events.driver.controller.async_get_provisioning_entry(
|
||||||
node.node_id
|
node.node_id
|
||||||
@@ -648,29 +648,37 @@ class ControllerEvents:
|
|||||||
provisioning_entry
|
provisioning_entry
|
||||||
and provisioning_entry.additional_properties
|
and provisioning_entry.additional_properties
|
||||||
and "device_id" in provisioning_entry.additional_properties
|
and "device_id" in provisioning_entry.additional_properties
|
||||||
):
|
and (
|
||||||
preprovisioned_device = self.dev_reg.async_get(
|
pre_provisioned_device := self.dev_reg.async_get(
|
||||||
provisioning_entry.additional_properties["device_id"]
|
provisioning_entry.additional_properties["device_id"]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
and (dsk_identifier := (DOMAIN, f"provision_{provisioning_entry.dsk}"))
|
||||||
|
in pre_provisioned_device.identifiers
|
||||||
|
):
|
||||||
|
driver = self.driver_events.driver
|
||||||
|
device_id = get_device_id(driver, node)
|
||||||
|
device_id_ext = get_device_id_ext(driver, node)
|
||||||
|
new_identifiers = pre_provisioned_device.identifiers.copy()
|
||||||
|
new_identifiers.remove(dsk_identifier)
|
||||||
|
new_identifiers.add(device_id)
|
||||||
|
if device_id_ext:
|
||||||
|
new_identifiers.add(device_id_ext)
|
||||||
|
|
||||||
if preprovisioned_device:
|
if self.dev_reg.async_get_device(identifiers=new_identifiers):
|
||||||
dsk = provisioning_entry.dsk
|
# If a device entry is registered with the node ID based identifiers,
|
||||||
dsk_identifier = (DOMAIN, f"provision_{dsk}")
|
# just remove the device entry with the DSK identifier.
|
||||||
|
self.dev_reg.async_update_device(
|
||||||
# If the pre-provisioned device has the DSK identifier, remove it
|
pre_provisioned_device.id,
|
||||||
if dsk_identifier in preprovisioned_device.identifiers:
|
remove_config_entry_id=self.config_entry.entry_id,
|
||||||
driver = self.driver_events.driver
|
)
|
||||||
device_id = get_device_id(driver, node)
|
else:
|
||||||
device_id_ext = get_device_id_ext(driver, node)
|
# Add the node ID based identifiers to the device entry
|
||||||
new_identifiers = preprovisioned_device.identifiers.copy()
|
# with the DSK identifier and remove the DSK identifier.
|
||||||
new_identifiers.remove(dsk_identifier)
|
self.dev_reg.async_update_device(
|
||||||
new_identifiers.add(device_id)
|
pre_provisioned_device.id,
|
||||||
if device_id_ext:
|
new_identifiers=new_identifiers,
|
||||||
new_identifiers.add(device_id_ext)
|
)
|
||||||
self.dev_reg.async_update_device(
|
|
||||||
preprovisioned_device.id,
|
|
||||||
new_identifiers=new_identifiers,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def async_register_node_in_dev_reg(self, node: ZwaveNode) -> dr.DeviceEntry:
|
async def async_register_node_in_dev_reg(self, node: ZwaveNode) -> dr.DeviceEntry:
|
||||||
"""Register node in dev reg."""
|
"""Register node in dev reg."""
|
||||||
|
@@ -497,17 +497,17 @@ async def test_on_node_added_ready(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_on_node_added_preprovisioned(
|
async def test_check_pre_provisioned_device_update_device(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device_registry: dr.DeviceRegistry,
|
device_registry: dr.DeviceRegistry,
|
||||||
multisensor_6_state,
|
multisensor_6_state: NodeDataType,
|
||||||
client,
|
client: MagicMock,
|
||||||
integration,
|
integration: MockConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test node added event with a preprovisioned device."""
|
"""Test check pre-provisioned device that should update the device."""
|
||||||
dsk = "test"
|
dsk = "test"
|
||||||
node = Node(client, deepcopy(multisensor_6_state))
|
node = Node(client, deepcopy(multisensor_6_state))
|
||||||
device = device_registry.async_get_or_create(
|
pre_provisioned_device = device_registry.async_get_or_create(
|
||||||
config_entry_id=integration.entry_id,
|
config_entry_id=integration.entry_id,
|
||||||
identifiers={(DOMAIN, f"provision_{dsk}")},
|
identifiers={(DOMAIN, f"provision_{dsk}")},
|
||||||
)
|
)
|
||||||
@@ -515,7 +515,7 @@ async def test_on_node_added_preprovisioned(
|
|||||||
{
|
{
|
||||||
"dsk": dsk,
|
"dsk": dsk,
|
||||||
"securityClasses": [SecurityClass.S2_UNAUTHENTICATED],
|
"securityClasses": [SecurityClass.S2_UNAUTHENTICATED],
|
||||||
"device_id": device.id,
|
"device_id": pre_provisioned_device.id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
with patch(
|
with patch(
|
||||||
@@ -526,14 +526,60 @@ async def test_on_node_added_preprovisioned(
|
|||||||
client.driver.controller.emit("node added", event)
|
client.driver.controller.emit("node added", event)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
device = device_registry.async_get(device.id)
|
device = device_registry.async_get(pre_provisioned_device.id)
|
||||||
assert device
|
assert device
|
||||||
assert device.identifiers == {
|
assert device.identifiers == {
|
||||||
get_device_id(client.driver, node),
|
get_device_id(client.driver, node),
|
||||||
get_device_id_ext(client.driver, node),
|
get_device_id_ext(client.driver, node),
|
||||||
}
|
}
|
||||||
assert device.sw_version == node.firmware_version
|
assert device.sw_version == node.firmware_version
|
||||||
# There should only be the controller and the preprovisioned device
|
# There should only be the controller and the pre-provisioned device
|
||||||
|
assert len(device_registry.devices) == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_check_pre_provisioned_device_remove_device(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_registry: dr.DeviceRegistry,
|
||||||
|
multisensor_6_state: NodeDataType,
|
||||||
|
client: MagicMock,
|
||||||
|
integration: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test check pre-provisioned device that should remove the device."""
|
||||||
|
dsk = "test"
|
||||||
|
driver = client.driver
|
||||||
|
node = Node(client, deepcopy(multisensor_6_state))
|
||||||
|
pre_provisioned_device = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=integration.entry_id,
|
||||||
|
identifiers={(DOMAIN, f"provision_{dsk}")},
|
||||||
|
)
|
||||||
|
extended_identifier = get_device_id_ext(driver, node)
|
||||||
|
assert extended_identifier
|
||||||
|
existing_device = device_registry.async_get_or_create(
|
||||||
|
config_entry_id=integration.entry_id,
|
||||||
|
identifiers={
|
||||||
|
get_device_id(driver, node),
|
||||||
|
extended_identifier,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
provisioning_entry = ProvisioningEntry.from_dict(
|
||||||
|
{
|
||||||
|
"dsk": dsk,
|
||||||
|
"securityClasses": [SecurityClass.S2_UNAUTHENTICATED],
|
||||||
|
"device_id": pre_provisioned_device.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with patch(
|
||||||
|
f"{CONTROLLER_PATCH_PREFIX}.async_get_provisioning_entry",
|
||||||
|
side_effect=lambda id: provisioning_entry if id == node.node_id else None,
|
||||||
|
):
|
||||||
|
event = {"node": node}
|
||||||
|
client.driver.controller.emit("node added", event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert not device_registry.async_get(pre_provisioned_device.id)
|
||||||
|
assert device_registry.async_get(existing_device.id)
|
||||||
|
|
||||||
|
# There should only be the controller and the existing device
|
||||||
assert len(device_registry.devices) == 2
|
assert len(device_registry.devices) == 2
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user