mirror of
https://github.com/home-assistant/core.git
synced 2025-09-03 03:41:40 +02:00
Handle Z-Wave RssiErrorReceived (#150846)
This commit is contained in:
@@ -4,15 +4,15 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections.abc import Callable, Mapping
|
from collections.abc import Callable, Mapping
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from zwave_js_server.const import CommandClass
|
from zwave_js_server.const import CommandClass, RssiError
|
||||||
from zwave_js_server.const.command_class.meter import (
|
from zwave_js_server.const.command_class.meter import (
|
||||||
RESET_METER_OPTION_TARGET_VALUE,
|
RESET_METER_OPTION_TARGET_VALUE,
|
||||||
RESET_METER_OPTION_TYPE,
|
RESET_METER_OPTION_TYPE,
|
||||||
)
|
)
|
||||||
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
from zwave_js_server.exceptions import BaseZwaveJSServerError, RssiErrorReceived
|
||||||
from zwave_js_server.model.controller import Controller
|
from zwave_js_server.model.controller import Controller
|
||||||
from zwave_js_server.model.controller.statistics import ControllerStatistics
|
from zwave_js_server.model.controller.statistics import ControllerStatistics
|
||||||
from zwave_js_server.model.driver import Driver
|
from zwave_js_server.model.driver import Driver
|
||||||
@@ -1049,7 +1049,7 @@ class ZWaveStatisticsSensor(SensorEntity):
|
|||||||
self,
|
self,
|
||||||
config_entry: ZwaveJSConfigEntry,
|
config_entry: ZwaveJSConfigEntry,
|
||||||
driver: Driver,
|
driver: Driver,
|
||||||
statistics_src: ZwaveNode | Controller,
|
statistics_src: Controller | ZwaveNode,
|
||||||
description: ZWaveJSStatisticsSensorEntityDescription,
|
description: ZWaveJSStatisticsSensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a Z-Wave statistics entity."""
|
"""Initialize a Z-Wave statistics entity."""
|
||||||
@@ -1080,13 +1080,31 @@ class ZWaveStatisticsSensor(SensorEntity):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def statistics_updated(self, event_data: dict) -> None:
|
def _statistics_updated(self, event_data: dict) -> None:
|
||||||
"""Call when statistics updated event is received."""
|
"""Call when statistics updated event is received."""
|
||||||
self._attr_native_value = self.entity_description.convert(
|
statistics = cast(
|
||||||
event_data["statistics_updated"], self.entity_description.key
|
ControllerStatistics | NodeStatistics, event_data["statistics_updated"]
|
||||||
)
|
)
|
||||||
|
self._set_statistics(statistics)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _set_statistics(
|
||||||
|
self, statistics: ControllerStatistics | NodeStatistics
|
||||||
|
) -> None:
|
||||||
|
"""Set updated statistics."""
|
||||||
|
try:
|
||||||
|
self._attr_native_value = self.entity_description.convert(
|
||||||
|
statistics, self.entity_description.key
|
||||||
|
)
|
||||||
|
except RssiErrorReceived as err:
|
||||||
|
if err.error is RssiError.NOT_AVAILABLE:
|
||||||
|
self._attr_available = False
|
||||||
|
return
|
||||||
|
self._attr_native_value = None
|
||||||
|
# Reset available state.
|
||||||
|
self._attr_available = True
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Call when entity is added."""
|
"""Call when entity is added."""
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
@@ -1104,10 +1122,8 @@ class ZWaveStatisticsSensor(SensorEntity):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
self.statistics_src.on("statistics updated", self.statistics_updated)
|
self.statistics_src.on("statistics updated", self._statistics_updated)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set initial state
|
# Set initial state
|
||||||
self._attr_native_value = self.entity_description.convert(
|
self._set_statistics(self.statistics_src.statistics)
|
||||||
self.statistics_src.statistics, self.entity_description.key
|
|
||||||
)
|
|
||||||
|
@@ -1045,6 +1045,183 @@ async def test_last_seen_statistics_sensors(
|
|||||||
assert state.state == "2024-01-01T12:00:00+00:00"
|
assert state.state == "2024-01-01T12:00:00+00:00"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_rssi_sensor_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
zp3111: Node,
|
||||||
|
integration: MockConfigEntry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
) -> None:
|
||||||
|
"""Test rssi sensor error."""
|
||||||
|
entity_id = "sensor.4_in_1_sensor_signal_strength"
|
||||||
|
|
||||||
|
entity_registry.async_update_entity(entity_id, disabled_by=None)
|
||||||
|
|
||||||
|
# reload integration and check if entity is correctly there
|
||||||
|
await hass.config_entries.async_reload(integration.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "unknown"
|
||||||
|
|
||||||
|
# Fire statistics updated event for node
|
||||||
|
event = Event(
|
||||||
|
"statistics updated",
|
||||||
|
{
|
||||||
|
"source": "node",
|
||||||
|
"event": "statistics updated",
|
||||||
|
"nodeId": zp3111.node_id,
|
||||||
|
"statistics": {
|
||||||
|
"commandsTX": 1,
|
||||||
|
"commandsRX": 2,
|
||||||
|
"commandsDroppedTX": 3,
|
||||||
|
"commandsDroppedRX": 4,
|
||||||
|
"timeoutResponse": 5,
|
||||||
|
"rtt": 6,
|
||||||
|
"rssi": 7, # baseline
|
||||||
|
"lwr": {
|
||||||
|
"protocolDataRate": 1,
|
||||||
|
"rssi": 1,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"nlwr": {
|
||||||
|
"protocolDataRate": 2,
|
||||||
|
"rssi": 2,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"lastSeen": "2024-01-01T00:00:00+0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
zp3111.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "7"
|
||||||
|
|
||||||
|
event = Event(
|
||||||
|
"statistics updated",
|
||||||
|
{
|
||||||
|
"source": "node",
|
||||||
|
"event": "statistics updated",
|
||||||
|
"nodeId": zp3111.node_id,
|
||||||
|
"statistics": {
|
||||||
|
"commandsTX": 1,
|
||||||
|
"commandsRX": 2,
|
||||||
|
"commandsDroppedTX": 3,
|
||||||
|
"commandsDroppedRX": 4,
|
||||||
|
"timeoutResponse": 5,
|
||||||
|
"rtt": 6,
|
||||||
|
"rssi": 125, # no signal detected
|
||||||
|
"lwr": {
|
||||||
|
"protocolDataRate": 1,
|
||||||
|
"rssi": 1,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"nlwr": {
|
||||||
|
"protocolDataRate": 2,
|
||||||
|
"rssi": 2,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"lastSeen": "2024-01-01T00:00:00+0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
zp3111.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "unknown"
|
||||||
|
|
||||||
|
event = Event(
|
||||||
|
"statistics updated",
|
||||||
|
{
|
||||||
|
"source": "node",
|
||||||
|
"event": "statistics updated",
|
||||||
|
"nodeId": zp3111.node_id,
|
||||||
|
"statistics": {
|
||||||
|
"commandsTX": 1,
|
||||||
|
"commandsRX": 2,
|
||||||
|
"commandsDroppedTX": 3,
|
||||||
|
"commandsDroppedRX": 4,
|
||||||
|
"timeoutResponse": 5,
|
||||||
|
"rtt": 6,
|
||||||
|
"rssi": 127, # not available
|
||||||
|
"lwr": {
|
||||||
|
"protocolDataRate": 1,
|
||||||
|
"rssi": 1,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"nlwr": {
|
||||||
|
"protocolDataRate": 2,
|
||||||
|
"rssi": 2,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"lastSeen": "2024-01-01T00:00:00+0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
zp3111.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "unavailable"
|
||||||
|
|
||||||
|
event = Event(
|
||||||
|
"statistics updated",
|
||||||
|
{
|
||||||
|
"source": "node",
|
||||||
|
"event": "statistics updated",
|
||||||
|
"nodeId": zp3111.node_id,
|
||||||
|
"statistics": {
|
||||||
|
"commandsTX": 1,
|
||||||
|
"commandsRX": 2,
|
||||||
|
"commandsDroppedTX": 3,
|
||||||
|
"commandsDroppedRX": 4,
|
||||||
|
"timeoutResponse": 5,
|
||||||
|
"rtt": 6,
|
||||||
|
"rssi": 126, # receiver saturated
|
||||||
|
"lwr": {
|
||||||
|
"protocolDataRate": 1,
|
||||||
|
"rssi": 1,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"nlwr": {
|
||||||
|
"protocolDataRate": 2,
|
||||||
|
"rssi": 2,
|
||||||
|
"repeaters": [],
|
||||||
|
"repeaterRSSI": [],
|
||||||
|
"routeFailedBetween": [],
|
||||||
|
},
|
||||||
|
"lastSeen": "2024-01-01T00:00:00+0000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
zp3111.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
assert state.state == "unknown"
|
||||||
|
|
||||||
|
|
||||||
ENERGY_PRODUCTION_ENTITY_MAP = {
|
ENERGY_PRODUCTION_ENTITY_MAP = {
|
||||||
"energy_production_power": {
|
"energy_production_power": {
|
||||||
"state": 1.23,
|
"state": 1.23,
|
||||||
|
Reference in New Issue
Block a user