From d39ef523b85cf26c3e30e66d554ea48d353dc131 Mon Sep 17 00:00:00 2001 From: AlCalzone Date: Thu, 26 Mar 2026 11:45:34 +0100 Subject: [PATCH] Revert: Create repair issue for legacy Z-Wave Door state sensors that are still in use (#166583) --- .../components/zwave_js/binary_sensor.py | 103 +------ .../components/zwave_js/strings.json | 4 - .../components/zwave_js/test_binary_sensor.py | 252 +----------------- 3 files changed, 7 insertions(+), 352 deletions(-) diff --git a/homeassistant/components/zwave_js/binary_sensor.py b/homeassistant/components/zwave_js/binary_sensor.py index 1af91d168f5..c0df675a25d 100644 --- a/homeassistant/components/zwave_js/binary_sensor.py +++ b/homeassistant/components/zwave_js/binary_sensor.py @@ -18,27 +18,17 @@ from zwave_js_server.const.command_class.notification import ( ) from zwave_js_server.model.driver import Driver -from homeassistant.components.automation import automations_with_entity from homeassistant.components.binary_sensor import ( DOMAIN as BINARY_SENSOR_DOMAIN, BinarySensorDeviceClass, BinarySensorEntity, BinarySensorEntityDescription, ) -from homeassistant.components.script import scripts_with_entity -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import EntityCategory, Platform from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import entity_registry as er from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from homeassistant.helpers.issue_registry import ( - IssueSeverity, - async_create_issue, - async_delete_issue, -) -from homeassistant.helpers.start import async_at_started from .const import DOMAIN from .entity import NewZwaveDiscoveryInfo, ZWaveBaseEntity @@ -413,93 +403,6 @@ def is_valid_notification_binary_sensor( return len(info.primary_value.metadata.states) > 1 -@callback -def _async_check_legacy_entity_repair( - hass: HomeAssistant, - driver: Driver, - entity: ZWaveLegacyDoorStateBinarySensor, -) -> None: - """Schedule a repair issue check once HA has fully started.""" - - @callback - def _async_do_check(hass: HomeAssistant) -> None: - """Create or delete a repair issue for a deprecated legacy door state entity.""" - ent_reg = er.async_get(hass) - if entity.unique_id is None: - return - entity_id = ent_reg.async_get_entity_id( - BINARY_SENSOR_DOMAIN, DOMAIN, entity.unique_id - ) - if entity_id is None: - return - - issue_id = f"deprecated_legacy_door_state.{entity_id}" - - # Delete any stale repair issue if the entity is disabled or missing — - # the user has already dealt with it. - entity_entry = ent_reg.async_get(entity_id) - if entity_entry is None or entity_entry.disabled: - async_delete_issue(hass, DOMAIN, issue_id) - return - - entity_automations = automations_with_entity(hass, entity_id) - entity_scripts = scripts_with_entity(hass, entity_id) - - # Delete any stale repair issue if the entity is no longer referenced - # in any automation or script. - if not entity_automations and not entity_scripts: - async_delete_issue(hass, DOMAIN, issue_id) - return - - opening_state_value = get_opening_state_notification_value( - entity.info.node, entity.info.primary_value.endpoint - ) - if opening_state_value is None: - async_delete_issue(hass, DOMAIN, issue_id) - return - opening_state_unique_id = ( - f"{driver.controller.home_id}.{opening_state_value.value_id}" - ) - opening_state_entity_id = ent_reg.async_get_entity_id( - SENSOR_DOMAIN, DOMAIN, opening_state_unique_id - ) - # Delete any stale repair issue if the replacement opening state sensor - # no longer exists for some reason - if opening_state_entity_id is None: - async_delete_issue(hass, DOMAIN, issue_id) - return - - items = [ - f"- [{item.name or item.original_name or eid}](/config/{domain}/edit/{item.unique_id})" - for domain, entity_ids in ( - ("automation", entity_automations), - ("script", entity_scripts), - ) - for eid in entity_ids - if (item := ent_reg.async_get(eid)) - ] - - async_create_issue( - hass, - DOMAIN, - issue_id, - is_fixable=False, - is_persistent=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_legacy_door_state", - translation_placeholders={ - "entity_id": entity_id, - "entity_name": entity_entry.name - or entity_entry.original_name - or entity_id, - "opening_state_entity_id": opening_state_entity_id, - "items": "\n".join(items), - }, - ) - - async_at_started(hass, _async_do_check) - - async def async_setup_entry( hass: HomeAssistant, config_entry: ZwaveJSConfigEntry, @@ -543,9 +446,9 @@ async def async_setup_entry( isinstance(info, NewZwaveDiscoveryInfo) and info.entity_class is ZWaveLegacyDoorStateBinarySensor ): - entity = ZWaveLegacyDoorStateBinarySensor(config_entry, driver, info) - entities.append(entity) - _async_check_legacy_entity_repair(hass, driver, entity) + entities.append( + ZWaveLegacyDoorStateBinarySensor(config_entry, driver, info) + ) elif isinstance(info, NewZwaveDiscoveryInfo): pass # other entity classes are not migrated yet elif info.platform_hint == "notification": diff --git a/homeassistant/components/zwave_js/strings.json b/homeassistant/components/zwave_js/strings.json index 21db14ed598..dbaefc4f1cf 100644 --- a/homeassistant/components/zwave_js/strings.json +++ b/homeassistant/components/zwave_js/strings.json @@ -303,10 +303,6 @@ } }, "issues": { - "deprecated_legacy_door_state": { - "description": "The binary sensor `{entity_id}` is deprecated because it has been replaced with the opening state sensor `{opening_state_entity_id}`.\n\nThe entity was found in the following automations or scripts:\n{items}\n\nPlease update the above automations or scripts to use the opening state sensor `{opening_state_entity_id}` and disable the binary sensor `{entity_id}` to fix this issue.\n\nNote that `{opening_state_entity_id}` reports three states:\n- Closed\n- Open\n- Tilted (if supported by the device).", - "title": "Deprecation: {entity_name}" - }, "device_config_file_changed": { "fix_flow": { "abort": { diff --git a/tests/components/zwave_js/test_binary_sensor.py b/tests/components/zwave_js/test_binary_sensor.py index 9a1ac6ad6b3..ad7db02950c 100644 --- a/tests/components/zwave_js/test_binary_sensor.py +++ b/tests/components/zwave_js/test_binary_sensor.py @@ -7,14 +7,9 @@ from unittest.mock import MagicMock import pytest from zwave_js_server.event import Event -from zwave_js_server.model.node import Node, NodeDataType +from zwave_js_server.model.node import Node -from homeassistant.components import automation -from homeassistant.components.binary_sensor import ( - DOMAIN as BINARY_SENSOR_DOMAIN, - BinarySensorDeviceClass, -) -from homeassistant.components.zwave_js.const import DOMAIN +from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY from homeassistant.const import ( ATTR_DEVICE_CLASS, @@ -25,9 +20,7 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er, issue_registry as ir -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue -from homeassistant.setup import async_setup_component +from homeassistant.helpers import entity_registry as er from homeassistant.util import dt as dt_util from .common import ( @@ -36,6 +29,7 @@ from .common import ( NOTIFICATION_MOTION_BINARY_SENSOR, PROPERTY_DOOR_STATUS_BINARY_SENSOR, TAMPER_SENSOR, + NodeDataType, ) from tests.common import MockConfigEntry, async_fire_time_changed @@ -989,241 +983,3 @@ async def test_hoppe_ehandle_connectsense( assert entry.original_name == "Window/door is tilted" assert entry.original_device_class == BinarySensorDeviceClass.WINDOW assert entry.disabled_by is None, "Entity should be enabled by default" - - -async def test_legacy_door_state_repair_issue( - hass: HomeAssistant, - entity_registry: er.EntityRegistry, - issue_registry: ir.IssueRegistry, - client: MagicMock, - hoppe_ehandle_connectsense_state: NodeDataType, -) -> None: - """Test repair issue is created only when legacy door state entity is in automation.""" - node = Node(client, hoppe_ehandle_connectsense_state) - client.driver.controller.nodes[node.node_id] = node - home_id = client.driver.controller.home_id - - # Pre-register the legacy entity as enabled (simulating existing user entity). - unique_id = f"{home_id}.20-113-0-Access Control-Door state.22" - entity_entry = entity_registry.async_get_or_create( - BINARY_SENSOR_DOMAIN, - DOMAIN, - unique_id, - suggested_object_id="ehandle_connectsense_window_door_is_open", - original_name="Window/door is open", - ) - entity_id = entity_entry.entity_id - - # Load the integration without any automation referencing the entity. - entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - # No repair issues should exist without automations. - issues = [ - issue - for issue in issue_registry.issues.values() - if issue.domain == DOMAIN - and issue.translation_key == "deprecated_legacy_door_state" - ] - assert len(issues) == 0 - - # Now set up an automation referencing the legacy entity. - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: { - "id": "test_automation", - "alias": "test", - "trigger": {"platform": "state", "entity_id": entity_id}, - "action": { - "action": "automation.turn_on", - "target": {"entity_id": "automation.test_automation"}, - }, - } - }, - ) - - # Reload the integration so the repair check runs again. - await hass.config_entries.async_reload(entry.entry_id) - await hass.async_block_till_done() - - issue = issue_registry.async_get_issue( - DOMAIN, f"deprecated_legacy_door_state.{entity_id}" - ) - assert issue is not None - assert issue.translation_key == "deprecated_legacy_door_state" - assert issue.translation_placeholders["entity_id"] == entity_id - assert issue.translation_placeholders["entity_name"] == "Window/door is open" - assert ( - issue.translation_placeholders["opening_state_entity_id"] - == "sensor.ehandle_connectsense_opening_state" - ) - assert "test" in issue.translation_placeholders["items"] - - -async def test_legacy_door_state_no_repair_issue_when_disabled( - hass: HomeAssistant, - entity_registry: er.EntityRegistry, - issue_registry: ir.IssueRegistry, - client: MagicMock, - hoppe_ehandle_connectsense_state: NodeDataType, -) -> None: - """Test no repair issue when legacy door state entity is disabled.""" - node = Node(client, hoppe_ehandle_connectsense_state) - client.driver.controller.nodes[node.node_id] = node - home_id = client.driver.controller.home_id - - # Pre-register the legacy entity as disabled. - unique_id = f"{home_id}.20-113-0-Access Control-Door state.22" - entity_entry = entity_registry.async_get_or_create( - BINARY_SENSOR_DOMAIN, - DOMAIN, - unique_id, - suggested_object_id="ehandle_connectsense_window_door_is_open", - original_name="Window/door is open", - disabled_by=er.RegistryEntryDisabler.INTEGRATION, - ) - entity_id = entity_entry.entity_id - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: { - "id": "test_automation", - "alias": "test", - "trigger": {"platform": "state", "entity_id": entity_id}, - "action": { - "action": "automation.turn_on", - "target": {"entity_id": "automation.test_automation"}, - }, - } - }, - ) - - entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - # No repair issue should be created since the entity is disabled. - issue = issue_registry.async_get_issue( - DOMAIN, f"deprecated_legacy_door_state.{entity_id}" - ) - assert issue is None - - -async def test_hoppe_custom_tilt_sensor_no_repair_issue( - hass: HomeAssistant, - entity_registry: er.EntityRegistry, - issue_registry: ir.IssueRegistry, - client: MagicMock, - hoppe_ehandle_connectsense_state: NodeDataType, -) -> None: - """Test no repair issue for Hoppe eHandle custom tilt sensor (Binary Sensor CC).""" - node = Node(client, hoppe_ehandle_connectsense_state) - client.driver.controller.nodes[node.node_id] = node - - # Pre-register the Hoppe tilt entity as enabled (simulating existing user entity). - home_id = client.driver.controller.home_id - unique_id = f"{home_id}.20-48-0-Tilt" - entity_entry = entity_registry.async_get_or_create( - BINARY_SENSOR_DOMAIN, - DOMAIN, - unique_id, - suggested_object_id="ehandle_connectsense_window_door_is_tilted", - original_name="Window/door is tilted", - ) - entity_id = entity_entry.entity_id - - # Set up automation referencing the custom tilt entity. - assert await async_setup_component( - hass, - automation.DOMAIN, - { - automation.DOMAIN: { - "id": "test_automation", - "alias": "test", - "trigger": {"platform": "state", "entity_id": entity_id}, - "action": { - "action": "automation.turn_on", - "target": {"entity_id": "automation.test_automation"}, - }, - } - }, - ) - - entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - # No repair issue should be created - this is a custom Binary Sensor CC entity, - # not a legacy Notification CC door state entity. - issue = issue_registry.async_get_issue( - DOMAIN, f"deprecated_legacy_door_state.{entity_id}" - ) - assert issue is None - - -async def test_legacy_door_state_stale_repair_issue_cleaned_up( - hass: HomeAssistant, - entity_registry: er.EntityRegistry, - issue_registry: ir.IssueRegistry, - client: MagicMock, - hoppe_ehandle_connectsense_state: NodeDataType, -) -> None: - """Test that a stale repair issue is deleted when there are no automations.""" - node = Node(client, hoppe_ehandle_connectsense_state) - client.driver.controller.nodes[node.node_id] = node - home_id = client.driver.controller.home_id - - # Pre-register the legacy entity as enabled. - unique_id = f"{home_id}.20-113-0-Access Control-Door state.22" - entity_entry = entity_registry.async_get_or_create( - BINARY_SENSOR_DOMAIN, - DOMAIN, - unique_id, - suggested_object_id="ehandle_connectsense_window_door_is_open", - original_name="Window/door is open", - ) - entity_id = entity_entry.entity_id - - # Seed a stale repair issue as if it had been created in a previous run. - async_create_issue( - hass, - DOMAIN, - f"deprecated_legacy_door_state.{entity_id}", - is_fixable=False, - is_persistent=False, - severity=IssueSeverity.WARNING, - translation_key="deprecated_legacy_door_state", - translation_placeholders={ - "entity_id": entity_id, - "entity_name": "Window/door is open", - "opening_state_entity_id": "sensor.ehandle_connectsense_opening_state", - "items": "- [test](/config/automation/edit/test_automation)", - }, - ) - assert ( - issue_registry.async_get_issue( - DOMAIN, f"deprecated_legacy_door_state.{entity_id}" - ) - is not None - ) - - # Load the integration with no automation referencing the legacy entity. - entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"}) - entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - # Stale issue should have been cleaned up. - assert ( - issue_registry.async_get_issue( - DOMAIN, f"deprecated_legacy_door_state.{entity_id}" - ) - is None - )