From 9bb78af79cad6b757520aa2946c385410753453e Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Sun, 16 Jan 2022 07:22:18 -0500 Subject: [PATCH] Ignore unavailable entities when creating zwave_js device actions list (#64184) * Fix bug with zwave-js device actions * outdent * Add test and fix bug * fix --- .../components/zwave_js/device_action.py | 18 +++++++++++---- .../components/zwave_js/test_device_action.py | 23 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/zwave_js/device_action.py b/homeassistant/components/zwave_js/device_action.py index f819a33f1d4..9f6fa7fc35c 100644 --- a/homeassistant/components/zwave_js/device_action.py +++ b/homeassistant/components/zwave_js/device_action.py @@ -21,6 +21,7 @@ from homeassistant.const import ( CONF_DOMAIN, CONF_ENTITY_ID, CONF_TYPE, + STATE_UNAVAILABLE, ) from homeassistant.core import Context, HomeAssistant from homeassistant.exceptions import HomeAssistantError @@ -172,7 +173,17 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: meter_endpoints: dict[int, dict[str, Any]] = defaultdict(dict) - for entry in entity_registry.async_entries_for_device(registry, device_id): + for entry in entity_registry.async_entries_for_device( + registry, device_id, include_disabled_entities=False + ): + # If an entry is unavailable, it is possible that the underlying value + # is no longer valid. Additionally, if an entry is disabled, its + # underlying value is not being monitored by HA so we shouldn't allow + # actions against it. + if ( + state := hass.states.get(entry.entity_id) + ) and state.state == STATE_UNAVAILABLE: + continue entity_action = {**base_action, CONF_ENTITY_ID: entry.entity_id} actions.append({**entity_action, CONF_TYPE: SERVICE_REFRESH_VALUE}) if entry.domain == LOCK_DOMAIN: @@ -187,10 +198,9 @@ async def async_get_actions(hass: HomeAssistant, device_id: str) -> list[dict]: value_id = entry.unique_id.split(".")[1] # If this unique ID doesn't have a value ID, we know it is the node status # sensor which doesn't have any relevant actions - if re.match(VALUE_ID_REGEX, value_id): - value = node.values[value_id] - else: + if not re.match(VALUE_ID_REGEX, value_id): continue + value = node.values[value_id] # If the value has the meterType CC specific value, we can add a reset_meter # action for it if CC_SPECIFIC_METER_TYPE in value.metadata.cc_specific: diff --git a/tests/components/zwave_js/test_device_action.py b/tests/components/zwave_js/test_device_action.py index 58f8091e2a1..5377d420268 100644 --- a/tests/components/zwave_js/test_device_action.py +++ b/tests/components/zwave_js/test_device_action.py @@ -12,6 +12,7 @@ from homeassistant.components.device_automation import DeviceAutomationType from homeassistant.components.zwave_js import DOMAIN, device_action from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.config_entries import ConfigEntry +from homeassistant.const import STATE_UNAVAILABLE from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, device_registry @@ -588,3 +589,25 @@ async def test_failure_scenarios( ) == {} ) + + +async def test_unavailable_entity_actions( + hass: HomeAssistant, + client: Client, + lock_schlage_be469: Node, + integration: ConfigEntry, +) -> None: + """Test unavailable entities are not included in actions list.""" + entity_id_unavailable = "binary_sensor.touchscreen_deadbolt_home_security_intrusion" + hass.states.async_set(entity_id_unavailable, STATE_UNAVAILABLE, force_update=True) + await hass.async_block_till_done() + node = lock_schlage_be469 + dev_reg = device_registry.async_get(hass) + device = dev_reg.async_get_device({get_device_id(client, node)}) + assert device + actions = await async_get_device_automations( + hass, DeviceAutomationType.ACTION, device.id + ) + assert not any( + action.get("entity_id") == entity_id_unavailable for action in actions + )