mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Handle invalid scale for zwave_js multilevel/meter sensors (#101173)
* Handle invalid scale for zwave_js multilevel/meter sensors * Remove logging statement
This commit is contained in:
parent
c951c03447
commit
383c63000e
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
from collections.abc import Iterable, Mapping
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any, TypeVar, cast
|
||||
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.const.command_class.energy_production import (
|
||||
@ -87,6 +87,7 @@ from zwave_js_server.const.command_class.multilevel_sensor import (
|
||||
MultilevelSensorScaleType,
|
||||
MultilevelSensorType,
|
||||
)
|
||||
from zwave_js_server.exceptions import UnknownValueData
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.model.value import (
|
||||
ConfigurationValue as ZwaveConfigurationValue,
|
||||
@ -355,24 +356,22 @@ class NumericSensorDataTemplateData:
|
||||
unit_of_measurement: str | None = None
|
||||
|
||||
|
||||
T = TypeVar(
|
||||
"T",
|
||||
MultilevelSensorType,
|
||||
MultilevelSensorScaleType,
|
||||
MeterScaleType,
|
||||
EnergyProductionParameter,
|
||||
EnergyProductionScaleType,
|
||||
)
|
||||
|
||||
|
||||
class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||
"""Data template class for Z-Wave Sensor entities."""
|
||||
|
||||
@staticmethod
|
||||
def find_key_from_matching_set(
|
||||
enum_value: MultilevelSensorType
|
||||
| MultilevelSensorScaleType
|
||||
| MeterScaleType
|
||||
| EnergyProductionParameter
|
||||
| EnergyProductionScaleType,
|
||||
set_map: Mapping[
|
||||
str,
|
||||
list[MultilevelSensorType]
|
||||
| list[MultilevelSensorScaleType]
|
||||
| list[MeterScaleType]
|
||||
| list[EnergyProductionScaleType]
|
||||
| list[EnergyProductionParameter],
|
||||
],
|
||||
enum_value: T, set_map: Mapping[str, list[T]]
|
||||
) -> str | None:
|
||||
"""Find a key in a set map that matches a given enum value."""
|
||||
for key, value_set in set_map.items():
|
||||
@ -393,7 +392,11 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||
return NumericSensorDataTemplateData(ENTITY_DESC_KEY_BATTERY, PERCENTAGE)
|
||||
|
||||
if value.command_class == CommandClass.METER:
|
||||
meter_scale_type = get_meter_scale_type(value)
|
||||
try:
|
||||
meter_scale_type = get_meter_scale_type(value)
|
||||
except UnknownValueData:
|
||||
return NumericSensorDataTemplateData()
|
||||
|
||||
unit = self.find_key_from_matching_set(meter_scale_type, METER_UNIT_MAP)
|
||||
# We do this because even though these are energy scales, they don't meet
|
||||
# the unit requirements for the energy device class.
|
||||
@ -418,8 +421,11 @@ class NumericSensorDataTemplate(BaseDiscoverySchemaDataTemplate):
|
||||
)
|
||||
|
||||
if value.command_class == CommandClass.SENSOR_MULTILEVEL:
|
||||
sensor_type = get_multilevel_sensor_type(value)
|
||||
multilevel_sensor_scale_type = get_multilevel_sensor_scale_type(value)
|
||||
try:
|
||||
sensor_type = get_multilevel_sensor_type(value)
|
||||
multilevel_sensor_scale_type = get_multilevel_sensor_scale_type(value)
|
||||
except UnknownValueData:
|
||||
return NumericSensorDataTemplateData()
|
||||
unit = self.find_key_from_matching_set(
|
||||
multilevel_sensor_scale_type, MULTILEVEL_SENSOR_UNIT_MAP
|
||||
)
|
||||
|
@ -130,6 +130,40 @@ async def test_numeric_sensor(
|
||||
assert state.state == "0"
|
||||
|
||||
|
||||
async def test_invalid_multilevel_sensor_scale(
|
||||
hass: HomeAssistant, client, multisensor_6_state, integration
|
||||
) -> None:
|
||||
"""Test a multilevel sensor with an invalid scale."""
|
||||
node_state = copy.deepcopy(multisensor_6_state)
|
||||
value = next(
|
||||
value
|
||||
for value in node_state["values"]
|
||||
if value["commandClass"] == 49 and value["property"] == "Air temperature"
|
||||
)
|
||||
value["metadata"]["ccSpecific"]["scale"] = -1
|
||||
value["metadata"]["unit"] = None
|
||||
|
||||
event = Event(
|
||||
"node added",
|
||||
{
|
||||
"source": "controller",
|
||||
"event": "node added",
|
||||
"node": node_state,
|
||||
"result": "",
|
||||
},
|
||||
)
|
||||
client.driver.controller.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(AIR_TEMPERATURE_SENSOR)
|
||||
|
||||
assert state
|
||||
assert state.state == "9.0"
|
||||
assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_STATE_CLASS not in state.attributes
|
||||
|
||||
|
||||
async def test_energy_sensors(
|
||||
hass: HomeAssistant, hank_binary_switch, integration
|
||||
) -> None:
|
||||
@ -424,10 +458,7 @@ async def test_node_status_sensor_not_ready(
|
||||
|
||||
|
||||
async def test_reset_meter(
|
||||
hass: HomeAssistant,
|
||||
client,
|
||||
aeon_smart_switch_6,
|
||||
integration,
|
||||
hass: HomeAssistant, client, aeon_smart_switch_6, integration
|
||||
) -> None:
|
||||
"""Test reset_meter service."""
|
||||
client.async_send_command.return_value = {}
|
||||
@ -487,10 +518,7 @@ async def test_reset_meter(
|
||||
|
||||
|
||||
async def test_meter_attributes(
|
||||
hass: HomeAssistant,
|
||||
client,
|
||||
aeon_smart_switch_6,
|
||||
integration,
|
||||
hass: HomeAssistant, client, aeon_smart_switch_6, integration
|
||||
) -> None:
|
||||
"""Test meter entity attributes."""
|
||||
state = hass.states.get(METER_ENERGY_SENSOR)
|
||||
@ -501,6 +529,42 @@ async def test_meter_attributes(
|
||||
assert state.attributes[ATTR_STATE_CLASS] is SensorStateClass.TOTAL_INCREASING
|
||||
|
||||
|
||||
async def test_invalid_meter_scale(
|
||||
hass: HomeAssistant, client, aeon_smart_switch_6_state, integration
|
||||
) -> None:
|
||||
"""Test a meter sensor with an invalid scale."""
|
||||
node_state = copy.deepcopy(aeon_smart_switch_6_state)
|
||||
value = next(
|
||||
value
|
||||
for value in node_state["values"]
|
||||
if value["commandClass"] == 50
|
||||
and value["property"] == "value"
|
||||
and value["propertyKey"] == 65537
|
||||
)
|
||||
value["metadata"]["ccSpecific"]["scale"] = -1
|
||||
value["metadata"]["unit"] = None
|
||||
|
||||
event = Event(
|
||||
"node added",
|
||||
{
|
||||
"source": "controller",
|
||||
"event": "node added",
|
||||
"node": node_state,
|
||||
"result": "",
|
||||
},
|
||||
)
|
||||
client.driver.controller.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(METER_ENERGY_SENSOR)
|
||||
assert state
|
||||
assert state.attributes[ATTR_METER_TYPE] == MeterType.ELECTRIC.value
|
||||
assert state.attributes[ATTR_METER_TYPE_NAME] == MeterType.ELECTRIC.name
|
||||
assert ATTR_DEVICE_CLASS not in state.attributes
|
||||
assert ATTR_STATE_CLASS not in state.attributes
|
||||
assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes
|
||||
|
||||
|
||||
async def test_special_meters(
|
||||
hass: HomeAssistant, aeon_smart_switch_6_state, client, integration
|
||||
) -> None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user