Skip to content

Commit cb8e076

Browse files
Petro31edenhaus
andauthored
Fix missing device_class and state_class on compensation entities (home-assistant#146115)
Co-authored-by: Robert Resch <[email protected]>
1 parent 73251fb commit cb8e076

File tree

3 files changed

+359
-196
lines changed

3 files changed

+359
-196
lines changed

homeassistant/components/compensation/__init__.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@
66
import numpy as np
77
import voluptuous as vol
88

9-
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
9+
from homeassistant.components.sensor import (
10+
CONF_STATE_CLASS,
11+
DEVICE_CLASSES_SCHEMA as SENSOR_DEVICE_CLASSES_SCHEMA,
12+
DOMAIN as SENSOR_DOMAIN,
13+
STATE_CLASSES_SCHEMA as SENSOR_STATE_CLASSES_SCHEMA,
14+
)
1015
from homeassistant.const import (
1116
CONF_ATTRIBUTE,
17+
CONF_DEVICE_CLASS,
1218
CONF_MAXIMUM,
1319
CONF_MINIMUM,
20+
CONF_NAME,
1421
CONF_SOURCE,
1522
CONF_UNIQUE_ID,
1623
CONF_UNIT_OF_MEASUREMENT,
@@ -50,20 +57,23 @@ def datapoints_greater_than_degree(value: dict) -> dict:
5057

5158
COMPENSATION_SCHEMA = vol.Schema(
5259
{
53-
vol.Required(CONF_SOURCE): cv.entity_id,
60+
vol.Optional(CONF_ATTRIBUTE): cv.string,
5461
vol.Required(CONF_DATAPOINTS): [
5562
vol.ExactSequence([vol.Coerce(float), vol.Coerce(float)])
5663
],
57-
vol.Optional(CONF_UNIQUE_ID): cv.string,
58-
vol.Optional(CONF_ATTRIBUTE): cv.string,
59-
vol.Optional(CONF_UPPER_LIMIT, default=False): cv.boolean,
60-
vol.Optional(CONF_LOWER_LIMIT, default=False): cv.boolean,
61-
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): cv.positive_int,
6264
vol.Optional(CONF_DEGREE, default=DEFAULT_DEGREE): vol.All(
6365
vol.Coerce(int),
6466
vol.Range(min=1, max=7),
6567
),
68+
vol.Optional(CONF_DEVICE_CLASS): SENSOR_DEVICE_CLASSES_SCHEMA,
69+
vol.Optional(CONF_LOWER_LIMIT, default=False): cv.boolean,
70+
vol.Optional(CONF_NAME): cv.string,
71+
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): cv.positive_int,
72+
vol.Required(CONF_SOURCE): cv.entity_id,
73+
vol.Optional(CONF_STATE_CLASS): SENSOR_STATE_CLASSES_SCHEMA,
74+
vol.Optional(CONF_UNIQUE_ID): cv.string,
6675
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
76+
vol.Optional(CONF_UPPER_LIMIT, default=False): cv.boolean,
6777
}
6878
)
6979

homeassistant/components/compensation/sensor.py

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,23 @@
77

88
import numpy as np
99

10-
from homeassistant.components.sensor import SensorEntity
10+
from homeassistant.components.sensor import (
11+
ATTR_STATE_CLASS,
12+
CONF_STATE_CLASS,
13+
SensorEntity,
14+
)
1115
from homeassistant.const import (
16+
ATTR_DEVICE_CLASS,
1217
ATTR_UNIT_OF_MEASUREMENT,
1318
CONF_ATTRIBUTE,
19+
CONF_DEVICE_CLASS,
1420
CONF_MAXIMUM,
1521
CONF_MINIMUM,
22+
CONF_NAME,
1623
CONF_SOURCE,
1724
CONF_UNIQUE_ID,
1825
CONF_UNIT_OF_MEASUREMENT,
26+
STATE_UNAVAILABLE,
1927
STATE_UNKNOWN,
2028
)
2129
from homeassistant.core import (
@@ -59,24 +67,13 @@ async def async_setup_platform(
5967

6068
source: str = conf[CONF_SOURCE]
6169
attribute: str | None = conf.get(CONF_ATTRIBUTE)
62-
name = f"{DEFAULT_NAME} {source}"
63-
if attribute is not None:
64-
name = f"{name} {attribute}"
70+
if not (name := conf.get(CONF_NAME)):
71+
name = f"{DEFAULT_NAME} {source}"
72+
if attribute is not None:
73+
name = f"{name} {attribute}"
6574

6675
async_add_entities(
67-
[
68-
CompensationSensor(
69-
conf.get(CONF_UNIQUE_ID),
70-
name,
71-
source,
72-
attribute,
73-
conf[CONF_PRECISION],
74-
conf[CONF_POLYNOMIAL],
75-
conf.get(CONF_UNIT_OF_MEASUREMENT),
76-
conf[CONF_MINIMUM],
77-
conf[CONF_MAXIMUM],
78-
)
79-
]
76+
[CompensationSensor(conf.get(CONF_UNIQUE_ID), name, source, attribute, conf)]
8077
)
8178

8279

@@ -91,23 +88,27 @@ def __init__(
9188
name: str,
9289
source: str,
9390
attribute: str | None,
94-
precision: int,
95-
polynomial: np.poly1d,
96-
unit_of_measurement: str | None,
97-
minimum: tuple[float, float] | None,
98-
maximum: tuple[float, float] | None,
91+
config: dict[str, Any],
9992
) -> None:
10093
"""Initialize the Compensation sensor."""
94+
95+
self._attr_name = name
10196
self._source_entity_id = source
102-
self._precision = precision
10397
self._source_attribute = attribute
104-
self._attr_native_unit_of_measurement = unit_of_measurement
98+
99+
self._precision = config[CONF_PRECISION]
100+
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
101+
102+
polynomial: np.poly1d = config[CONF_POLYNOMIAL]
105103
self._poly = polynomial
106104
self._coefficients = polynomial.coefficients.tolist()
105+
107106
self._attr_unique_id = unique_id
108-
self._attr_name = name
109-
self._minimum = minimum
110-
self._maximum = maximum
107+
self._minimum = config[CONF_MINIMUM]
108+
self._maximum = config[CONF_MAXIMUM]
109+
110+
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
111+
self._attr_state_class = config.get(CONF_STATE_CLASS)
111112

112113
async def async_added_to_hass(self) -> None:
113114
"""Handle added to Hass."""
@@ -137,13 +138,40 @@ def _async_compensation_sensor_state_listener(
137138
"""Handle sensor state changes."""
138139
new_state: State | None
139140
if (new_state := event.data["new_state"]) is None:
141+
_LOGGER.warning(
142+
"While updating compensation %s, the new_state is None", self.name
143+
)
144+
self._attr_native_value = None
145+
self.async_write_ha_state()
140146
return
141147

148+
if new_state.state == STATE_UNKNOWN:
149+
self._attr_native_value = None
150+
self.async_write_ha_state()
151+
return
152+
153+
if new_state.state == STATE_UNAVAILABLE:
154+
self._attr_available = False
155+
self.async_write_ha_state()
156+
return
157+
158+
self._attr_available = True
159+
142160
if self.native_unit_of_measurement is None and self._source_attribute is None:
143161
self._attr_native_unit_of_measurement = new_state.attributes.get(
144162
ATTR_UNIT_OF_MEASUREMENT
145163
)
146164

165+
if self._attr_device_class is None and (
166+
device_class := new_state.attributes.get(ATTR_DEVICE_CLASS)
167+
):
168+
self._attr_device_class = device_class
169+
170+
if self._attr_state_class is None and (
171+
state_class := new_state.attributes.get(ATTR_STATE_CLASS)
172+
):
173+
self._attr_state_class = state_class
174+
147175
if self._source_attribute:
148176
value = new_state.attributes.get(self._source_attribute)
149177
else:

0 commit comments

Comments
 (0)