mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 16:27:08 +00:00
Keep entities of dead Z-Wave devices available (#148611)
This commit is contained in:
parent
87e641bf59
commit
ad881d892b
@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from zwave_js_server.const import NodeStatus
|
||||
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import (
|
||||
@ -27,8 +26,6 @@ from .discovery import ZwaveDiscoveryInfo
|
||||
from .helpers import get_device_id, get_unique_id, get_valueless_base_unique_id
|
||||
|
||||
EVENT_VALUE_REMOVED = "value removed"
|
||||
EVENT_DEAD = "dead"
|
||||
EVENT_ALIVE = "alive"
|
||||
|
||||
|
||||
class ZWaveBaseEntity(Entity):
|
||||
@ -141,11 +138,6 @@ class ZWaveBaseEntity(Entity):
|
||||
)
|
||||
)
|
||||
|
||||
for status_event in (EVENT_ALIVE, EVENT_DEAD):
|
||||
self.async_on_remove(
|
||||
self.info.node.on(status_event, self._node_status_alive_or_dead)
|
||||
)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
@ -211,19 +203,7 @@ class ZWaveBaseEntity(Entity):
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return entity availability."""
|
||||
return (
|
||||
self.driver.client.connected
|
||||
and bool(self.info.node.ready)
|
||||
and self.info.node.status != NodeStatus.DEAD
|
||||
)
|
||||
|
||||
@callback
|
||||
def _node_status_alive_or_dead(self, event_data: dict) -> None:
|
||||
"""Call when node status changes to alive or dead.
|
||||
|
||||
Should not be overridden by subclasses.
|
||||
"""
|
||||
self.async_write_ha_state()
|
||||
return self.driver.client.connected and bool(self.info.node.ready)
|
||||
|
||||
@callback
|
||||
def _value_changed(self, event_data: dict) -> None:
|
||||
|
@ -199,18 +199,13 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
||||
)
|
||||
return
|
||||
|
||||
# If device is asleep/dead, wait for it to wake up/become alive before
|
||||
# attempting an update
|
||||
for status, event_name in (
|
||||
(NodeStatus.ASLEEP, "wake up"),
|
||||
(NodeStatus.DEAD, "alive"),
|
||||
):
|
||||
if self.node.status == status:
|
||||
if not self._status_unsub:
|
||||
self._status_unsub = self.node.once(
|
||||
event_name, self._update_on_status_change
|
||||
)
|
||||
return
|
||||
# If device is asleep, wait for it to wake up before attempting an update
|
||||
if self.node.status == NodeStatus.ASLEEP:
|
||||
if not self._status_unsub:
|
||||
self._status_unsub = self.node.once(
|
||||
"wake up", self._update_on_status_change
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# Retrieve all firmware updates including non-stable ones but filter
|
||||
|
@ -37,7 +37,11 @@ from homeassistant.helpers import (
|
||||
)
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .common import AIR_TEMPERATURE_SENSOR, EATON_RF9640_ENTITY
|
||||
from .common import (
|
||||
AIR_TEMPERATURE_SENSOR,
|
||||
BULB_6_MULTI_COLOR_LIGHT_ENTITY,
|
||||
EATON_RF9640_ENTITY,
|
||||
)
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
@ -2168,3 +2172,39 @@ async def test_factory_reset_node(
|
||||
assert len(notifications) == 1
|
||||
assert list(notifications)[0] == msg_id
|
||||
assert "network with the home ID `3245146787`" in notifications[msg_id]["message"]
|
||||
|
||||
|
||||
async def test_entity_available_when_node_dead(
|
||||
hass: HomeAssistant, client, bulb_6_multi_color, integration
|
||||
) -> None:
|
||||
"""Test that entities remain available even when the node is dead."""
|
||||
|
||||
node = bulb_6_multi_color
|
||||
state = hass.states.get(BULB_6_MULTI_COLOR_LIGHT_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# Send dead event to the node
|
||||
event = Event(
|
||||
"dead", data={"source": "node", "event": "dead", "nodeId": node.node_id}
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Entity should remain available even though the node is dead
|
||||
state = hass.states.get(BULB_6_MULTI_COLOR_LIGHT_ENTITY)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# Send alive event to bring the node back
|
||||
event = Event(
|
||||
"alive", data={"source": "node", "event": "alive", "nodeId": node.node_id}
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Entity should still be available
|
||||
state = hass.states.get(BULB_6_MULTI_COLOR_LIGHT_ENTITY)
|
||||
assert state
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
@ -28,7 +28,7 @@ from homeassistant.components.zwave_js.lock import (
|
||||
SERVICE_SET_LOCK_CONFIGURATION,
|
||||
SERVICE_SET_LOCK_USERCODE,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, STATE_UNKNOWN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
@ -295,7 +295,8 @@ async def test_door_lock(
|
||||
assert node.status == NodeStatus.DEAD
|
||||
state = hass.states.get(SCHLAGE_BE469_LOCK_ENTITY)
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
# The state should still be locked, even if the node is dead
|
||||
assert state.state == LockState.LOCKED
|
||||
|
||||
|
||||
async def test_only_one_lock(
|
||||
|
@ -277,7 +277,7 @@ async def test_update_entity_dead(
|
||||
zen_31,
|
||||
integration,
|
||||
) -> None:
|
||||
"""Test update occurs when device is dead after it becomes alive."""
|
||||
"""Test update occurs even when device is dead."""
|
||||
event = Event(
|
||||
"dead",
|
||||
data={"source": "node", "event": "dead", "nodeId": zen_31.node_id},
|
||||
@ -290,17 +290,7 @@ async def test_update_entity_dead(
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5, days=1))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Because node is asleep we shouldn't attempt to check for firmware updates
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
event = Event(
|
||||
"alive",
|
||||
data={"source": "node", "event": "alive", "nodeId": zen_31.node_id},
|
||||
)
|
||||
zen_31.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Now that the node is up we can check for updates
|
||||
# Checking for firmware updates should proceed even for dead nodes
|
||||
assert len(client.async_send_command.call_args_list) > 0
|
||||
|
||||
args = client.async_send_command.call_args_list[0][0][0]
|
||||
|
Loading…
x
Reference in New Issue
Block a user