mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17:10 +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 collections.abc import Sequence
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from zwave_js_server.const import NodeStatus
|
|
||||||
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
||||||
from zwave_js_server.model.driver import Driver
|
from zwave_js_server.model.driver import Driver
|
||||||
from zwave_js_server.model.value import (
|
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
|
from .helpers import get_device_id, get_unique_id, get_valueless_base_unique_id
|
||||||
|
|
||||||
EVENT_VALUE_REMOVED = "value removed"
|
EVENT_VALUE_REMOVED = "value removed"
|
||||||
EVENT_DEAD = "dead"
|
|
||||||
EVENT_ALIVE = "alive"
|
|
||||||
|
|
||||||
|
|
||||||
class ZWaveBaseEntity(Entity):
|
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(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
@ -211,19 +203,7 @@ class ZWaveBaseEntity(Entity):
|
|||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return entity availability."""
|
"""Return entity availability."""
|
||||||
return (
|
return self.driver.client.connected and bool(self.info.node.ready)
|
||||||
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()
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _value_changed(self, event_data: dict) -> None:
|
def _value_changed(self, event_data: dict) -> None:
|
||||||
|
@ -199,16 +199,11 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# If device is asleep/dead, wait for it to wake up/become alive before
|
# If device is asleep, wait for it to wake up before attempting an update
|
||||||
# attempting an update
|
if self.node.status == NodeStatus.ASLEEP:
|
||||||
for status, event_name in (
|
|
||||||
(NodeStatus.ASLEEP, "wake up"),
|
|
||||||
(NodeStatus.DEAD, "alive"),
|
|
||||||
):
|
|
||||||
if self.node.status == status:
|
|
||||||
if not self._status_unsub:
|
if not self._status_unsub:
|
||||||
self._status_unsub = self.node.once(
|
self._status_unsub = self.node.once(
|
||||||
event_name, self._update_on_status_change
|
"wake up", self._update_on_status_change
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -37,7 +37,11 @@ from homeassistant.helpers import (
|
|||||||
)
|
)
|
||||||
from homeassistant.setup import async_setup_component
|
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 (
|
from tests.common import (
|
||||||
MockConfigEntry,
|
MockConfigEntry,
|
||||||
@ -2168,3 +2172,39 @@ async def test_factory_reset_node(
|
|||||||
assert len(notifications) == 1
|
assert len(notifications) == 1
|
||||||
assert list(notifications)[0] == msg_id
|
assert list(notifications)[0] == msg_id
|
||||||
assert "network with the home ID `3245146787`" in notifications[msg_id]["message"]
|
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_CONFIGURATION,
|
||||||
SERVICE_SET_LOCK_USERCODE,
|
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.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
@ -295,7 +295,8 @@ async def test_door_lock(
|
|||||||
assert node.status == NodeStatus.DEAD
|
assert node.status == NodeStatus.DEAD
|
||||||
state = hass.states.get(SCHLAGE_BE469_LOCK_ENTITY)
|
state = hass.states.get(SCHLAGE_BE469_LOCK_ENTITY)
|
||||||
assert state
|
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(
|
async def test_only_one_lock(
|
||||||
|
@ -277,7 +277,7 @@ async def test_update_entity_dead(
|
|||||||
zen_31,
|
zen_31,
|
||||||
integration,
|
integration,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test update occurs when device is dead after it becomes alive."""
|
"""Test update occurs even when device is dead."""
|
||||||
event = Event(
|
event = Event(
|
||||||
"dead",
|
"dead",
|
||||||
data={"source": "node", "event": "dead", "nodeId": zen_31.node_id},
|
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))
|
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=5, days=1))
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# Because node is asleep we shouldn't attempt to check for firmware updates
|
# Checking for firmware updates should proceed even for dead nodes
|
||||||
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
|
|
||||||
assert len(client.async_send_command.call_args_list) > 0
|
assert len(client.async_send_command.call_args_list) > 0
|
||||||
|
|
||||||
args = client.async_send_command.call_args_list[0][0][0]
|
args = client.async_send_command.call_args_list[0][0][0]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user