From edcd79749d1110ba1beaf05a38249332937b0b17 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 14:28:24 -0500 Subject: [PATCH 1/5] Set entity category for isy auxiliary sensors - Always create the error count sensor (disabled by default) --- homeassistant/components/isy994/sensor.py | 35 ++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 2dee990c24964c..0b8d24fb204139 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -3,7 +3,7 @@ from typing import Any, cast -from pyisy.constants import COMMAND_FRIENDLY_NAME, ISY_VALUE_UNKNOWN +from pyisy.constants import COMMAND_FRIENDLY_NAME, ISY_VALUE_UNKNOWN, PROP_COMMS_ERROR from pyisy.helpers import NodeProperty from pyisy.nodes import Node @@ -15,6 +15,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import ( @@ -33,7 +34,7 @@ from .helpers import convert_isy_value_to_hass, migrate_old_unique_ids # Disable general purpose and redundant sensors by default -AUX_DISABLED_BY_DEFAULT = ["ERR", "GV", "CLIEMD", "CLIHCS", "DO", "OL", "RR", "ST"] +AUX_DISABLED_BY_DEFAULT = ["GV", "CLIEMD", "CLIHCS", "DO", "OL", "RR", "ST"] ISY_CONTROL_TO_DEVICE_CLASS = { "BARPRES": SensorDeviceClass.PRESSURE, @@ -45,6 +46,11 @@ "LUMIN": SensorDeviceClass.ILLUMINANCE, "PF": SensorDeviceClass.POWER_FACTOR, } +ISY_CONTROL_TO_ENTITY_CATEGORY = { + "RR": EntityCategory.CONFIG, + "OL": EntityCategory.CONFIG, + PROP_COMMS_ERROR: EntityCategory.DIAGNOSTIC, +} async def async_setup_entry( @@ -57,8 +63,11 @@ async def async_setup_entry( for node in hass_isy_data[ISY994_NODES][SENSOR]: _LOGGER.debug("Loading %s", node.name) entities.append(ISYSensorEntity(node)) + entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False)) for node, control in hass_isy_data[ISY994_NODES][SENSOR_AUX]: + if control == PROP_COMMS_ERROR: + continue _LOGGER.debug("Loading %s %s", node.name, node.aux_properties[control]) enabled_default = not any( control.startswith(match) for match in AUX_DISABLED_BY_DEFAULT @@ -76,7 +85,7 @@ class ISYSensorEntity(ISYNodeEntity, SensorEntity): """Representation of an ISY994 sensor device.""" @property - def target(self) -> Node | NodeProperty: + def target(self) -> Node | NodeProperty | None: """Return target for the sensor.""" return self._node @@ -88,6 +97,9 @@ def target_value(self) -> Any: @property def raw_unit_of_measurement(self) -> dict | str | None: """Get the raw unit of measurement for the ISY994 sensor device.""" + if self.target is None: + return None + uom = self.target.uom # Backwards compatibility for ISYv4 Firmware: @@ -107,6 +119,9 @@ def raw_unit_of_measurement(self) -> dict | str | None: @property def native_value(self) -> float | int | str | None: """Get the state of the ISY994 sensor device.""" + if self.target is None: + return None + if (value := self.target_value) == ISY_VALUE_UNKNOWN: return None @@ -157,21 +172,21 @@ def __init__(self, node: Node, control: str, enabled_default: bool) -> None: super().__init__(node) self._control = control self._attr_entity_registry_enabled_default = enabled_default + self._attr_entity_category = ISY_CONTROL_TO_ENTITY_CATEGORY.get(control) + self._attr_device_class = ISY_CONTROL_TO_DEVICE_CLASS.get(control) @property - def device_class(self) -> SensorDeviceClass | str | None: - """Return the device class for the sensor.""" - return ISY_CONTROL_TO_DEVICE_CLASS.get(self._control, super().device_class) - - @property - def target(self) -> Node | NodeProperty: + def target(self) -> Node | NodeProperty | None: """Return target for the sensor.""" + if self._control not in self._node.aux_properties: + # Property not yet set (i.e. no errors) + return None return cast(NodeProperty, self._node.aux_properties[self._control]) @property def target_value(self) -> Any: """Return the target value.""" - return self.target.value + return None if self.target is None else self.target.value @property def unique_id(self) -> str | None: From 142527ae4b6f5b9adec5e57281230f955d332f24 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 14:40:11 -0500 Subject: [PATCH 2/5] merge --- homeassistant/components/isy994/sensor.py | 49 +++++++++++++++++------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 0b8d24fb204139..7621da25b334bd 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -3,7 +3,20 @@ from typing import Any, cast -from pyisy.constants import COMMAND_FRIENDLY_NAME, ISY_VALUE_UNKNOWN, PROP_COMMS_ERROR +from pyisy.constants import ( + COMMAND_FRIENDLY_NAME, + ISY_VALUE_UNKNOWN, + PROP_BATTERY_LEVEL, + PROP_BUSY, + PROP_COMMS_ERROR, + PROP_ENERGY_MODE, + PROP_HEAT_COOL_STATE, + PROP_HUMIDITY, + PROP_ON_LEVEL, + PROP_RAMP_RATE, + PROP_STATUS, + PROP_TEMPERATURE, +) from pyisy.helpers import NodeProperty from pyisy.nodes import Node @@ -34,21 +47,29 @@ from .helpers import convert_isy_value_to_hass, migrate_old_unique_ids # Disable general purpose and redundant sensors by default -AUX_DISABLED_BY_DEFAULT = ["GV", "CLIEMD", "CLIHCS", "DO", "OL", "RR", "ST"] +AUX_DISABLED_BY_DEFAULT_MATCH = ["GV", "DO"] +AUX_DISABLED_BY_DEFAULT_EXACT = { + PROP_ENERGY_MODE, + PROP_HEAT_COOL_STATE, + PROP_ON_LEVEL, + PROP_RAMP_RATE, + PROP_STATUS, +} +SKIP_AUX_PROPERTIES = {PROP_BUSY, PROP_COMMS_ERROR, PROP_STATUS} ISY_CONTROL_TO_DEVICE_CLASS = { + PROP_BATTERY_LEVEL: SensorDeviceClass.BATTERY, + PROP_HUMIDITY: SensorDeviceClass.HUMIDITY, + PROP_TEMPERATURE: SensorDeviceClass.TEMPERATURE, "BARPRES": SensorDeviceClass.PRESSURE, - "BATLVL": SensorDeviceClass.BATTERY, - "CLIHUM": SensorDeviceClass.HUMIDITY, - "CLITEMP": SensorDeviceClass.TEMPERATURE, "CO2LVL": SensorDeviceClass.CO2, "CV": SensorDeviceClass.VOLTAGE, "LUMIN": SensorDeviceClass.ILLUMINANCE, "PF": SensorDeviceClass.POWER_FACTOR, } ISY_CONTROL_TO_ENTITY_CATEGORY = { - "RR": EntityCategory.CONFIG, - "OL": EntityCategory.CONFIG, + PROP_RAMP_RATE: EntityCategory.CONFIG, + PROP_ON_LEVEL: EntityCategory.CONFIG, PROP_COMMS_ERROR: EntityCategory.DIAGNOSTIC, } @@ -62,15 +83,19 @@ async def async_setup_entry( for node in hass_isy_data[ISY994_NODES][SENSOR]: _LOGGER.debug("Loading %s", node.name) - entities.append(ISYSensorEntity(node)) - entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False)) + entities.extend( + [ + ISYSensorEntity(node), + ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False), + ] + ) for node, control in hass_isy_data[ISY994_NODES][SENSOR_AUX]: - if control == PROP_COMMS_ERROR: + if control in SKIP_AUX_PROPERTIES: continue _LOGGER.debug("Loading %s %s", node.name, node.aux_properties[control]) - enabled_default = not any( - control.startswith(match) for match in AUX_DISABLED_BY_DEFAULT + enabled_default = control not in AUX_DISABLED_BY_DEFAULT_EXACT and not any( + control.startswith(match) for match in AUX_DISABLED_BY_DEFAULT_MATCH ) entities.append(ISYAuxSensorEntity(node, control, enabled_default)) From f0bfe8402856288169c6c3c74fda57b7ec59c70f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 14:45:49 -0500 Subject: [PATCH 3/5] fix --- homeassistant/components/isy994/sensor.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index 7621da25b334bd..deaed736008d70 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -83,14 +83,11 @@ async def async_setup_entry( for node in hass_isy_data[ISY994_NODES][SENSOR]: _LOGGER.debug("Loading %s", node.name) - entities.extend( - [ - ISYSensorEntity(node), - ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False), - ] - ) + entities.append(ISYSensorEntity(node)) + aux_nodes = set() for node, control in hass_isy_data[ISY994_NODES][SENSOR_AUX]: + aux_nodes.add(node) if control in SKIP_AUX_PROPERTIES: continue _LOGGER.debug("Loading %s %s", node.name, node.aux_properties[control]) @@ -99,6 +96,10 @@ async def async_setup_entry( ) entities.append(ISYAuxSensorEntity(node, control, enabled_default)) + for node in aux_nodes: + # Anything a node in SENSOR_AUX can potentially have communication errors + entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False)) + for vname, vobj in hass_isy_data[ISY994_VARIABLES]: entities.append(ISYSensorVariableEntity(vname, vobj)) From 052f3da469185fda4b9a802aae8cb62147b70736 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 14:51:49 -0500 Subject: [PATCH 4/5] Update homeassistant/components/isy994/sensor.py --- homeassistant/components/isy994/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index deaed736008d70..957acf0b8cc29e 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -97,7 +97,7 @@ async def async_setup_entry( entities.append(ISYAuxSensorEntity(node, control, enabled_default)) for node in aux_nodes: - # Anything a node in SENSOR_AUX can potentially have communication errors + # Any node in SENSOR_AUX can potentially have communication errors entities.append(ISYAuxSensorEntity(node, PROP_COMMS_ERROR, False)) for vname, vobj in hass_isy_data[ISY994_VARIABLES]: From e504c6f7004ed73adaaaccde6dfb681d69c2dc2f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 3 May 2022 14:56:40 -0500 Subject: [PATCH 5/5] set state class as well --- homeassistant/components/isy994/sensor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/homeassistant/components/isy994/sensor.py b/homeassistant/components/isy994/sensor.py index deaed736008d70..cbfd3c37cacb5c 100644 --- a/homeassistant/components/isy994/sensor.py +++ b/homeassistant/components/isy994/sensor.py @@ -24,6 +24,7 @@ DOMAIN as SENSOR, SensorDeviceClass, SensorEntity, + SensorStateClass, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT @@ -67,6 +68,9 @@ "LUMIN": SensorDeviceClass.ILLUMINANCE, "PF": SensorDeviceClass.POWER_FACTOR, } +ISY_CONTROL_TO_STATE_CLASS = { + control: SensorStateClass.MEASUREMENT for control in ISY_CONTROL_TO_DEVICE_CLASS +} ISY_CONTROL_TO_ENTITY_CATEGORY = { PROP_RAMP_RATE: EntityCategory.CONFIG, PROP_ON_LEVEL: EntityCategory.CONFIG, @@ -200,6 +204,7 @@ def __init__(self, node: Node, control: str, enabled_default: bool) -> None: self._attr_entity_registry_enabled_default = enabled_default self._attr_entity_category = ISY_CONTROL_TO_ENTITY_CATEGORY.get(control) self._attr_device_class = ISY_CONTROL_TO_DEVICE_CLASS.get(control) + self._attr_state_class = ISY_CONTROL_TO_STATE_CLASS.get(control) @property def target(self) -> Node | NodeProperty | None: