mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Avoid creating Prometheus metrics for non-numeric states (#127262)
This commit is contained in:
parent
cca6965cd1
commit
6d48316436
@ -334,8 +334,8 @@ class PrometheusMetrics:
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def state_as_number(state: State) -> float:
|
||||
"""Return a state casted to a float."""
|
||||
def state_as_number(state: State) -> float | None:
|
||||
"""Return state as a float, or None if state cannot be converted."""
|
||||
try:
|
||||
if state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP:
|
||||
value = as_timestamp(state.state)
|
||||
@ -343,7 +343,7 @@ class PrometheusMetrics:
|
||||
value = state_helper.state_as_number(state)
|
||||
except ValueError:
|
||||
_LOGGER.debug("Could not convert %s to float", state)
|
||||
value = 0
|
||||
value = None
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
@ -373,8 +373,8 @@ class PrometheusMetrics:
|
||||
prometheus_client.Gauge,
|
||||
"State of the binary sensor (0/1)",
|
||||
)
|
||||
value = self.state_as_number(state)
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _handle_input_boolean(self, state: State) -> None:
|
||||
metric = self._metric(
|
||||
@ -382,8 +382,8 @@ class PrometheusMetrics:
|
||||
prometheus_client.Gauge,
|
||||
"State of the input boolean (0/1)",
|
||||
)
|
||||
value = self.state_as_number(state)
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _numeric_handler(self, state: State, domain: str, title: str) -> None:
|
||||
if unit := self._unit_string(state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)):
|
||||
@ -399,8 +399,7 @@ class PrometheusMetrics:
|
||||
f"State of the {title}",
|
||||
)
|
||||
|
||||
with suppress(ValueError):
|
||||
value = self.state_as_number(state)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
if (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfTemperature.FAHRENHEIT
|
||||
@ -422,15 +421,15 @@ class PrometheusMetrics:
|
||||
prometheus_client.Gauge,
|
||||
"State of the device tracker (0/1)",
|
||||
)
|
||||
value = self.state_as_number(state)
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _handle_person(self, state: State) -> None:
|
||||
metric = self._metric(
|
||||
"person_state", prometheus_client.Gauge, "State of the person (0/1)"
|
||||
)
|
||||
value = self.state_as_number(state)
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _handle_cover(self, state: State) -> None:
|
||||
metric = self._metric(
|
||||
@ -471,23 +470,19 @@ class PrometheusMetrics:
|
||||
"Light brightness percentage (0..100)",
|
||||
)
|
||||
|
||||
try:
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
brightness = state.attributes.get(ATTR_BRIGHTNESS)
|
||||
if state.state == STATE_ON and brightness is not None:
|
||||
value = brightness / 255.0
|
||||
else:
|
||||
value = self.state_as_number(state)
|
||||
value = float(brightness) / 255.0
|
||||
value = value * 100
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def _handle_lock(self, state: State) -> None:
|
||||
metric = self._metric(
|
||||
"lock_state", prometheus_client.Gauge, "State of the lock (0/1)"
|
||||
)
|
||||
value = self.state_as_number(state)
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _handle_climate_temp(
|
||||
self, state: State, attr: str, metric_name: str, metric_description: str
|
||||
@ -599,11 +594,8 @@ class PrometheusMetrics:
|
||||
prometheus_client.Gauge,
|
||||
"State of the humidifier (0/1)",
|
||||
)
|
||||
try:
|
||||
value = self.state_as_number(state)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
current_mode = state.attributes.get(ATTR_MODE)
|
||||
available_modes = state.attributes.get(ATTR_AVAILABLE_MODES)
|
||||
@ -634,8 +626,7 @@ class PrometheusMetrics:
|
||||
|
||||
_metric = self._metric(metric, prometheus_client.Gauge, documentation)
|
||||
|
||||
try:
|
||||
value = self.state_as_number(state)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
if (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
== UnitOfTemperature.FAHRENHEIT
|
||||
@ -644,8 +635,6 @@ class PrometheusMetrics:
|
||||
value, UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS
|
||||
)
|
||||
_metric.labels(**self._labels(state)).set(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self._battery(state)
|
||||
|
||||
@ -684,14 +673,9 @@ class PrometheusMetrics:
|
||||
@staticmethod
|
||||
def _sensor_fallback_metric(state: State, unit: str | None) -> str | None:
|
||||
"""Get metric from fallback logic for compatibility."""
|
||||
if unit in (None, ""):
|
||||
try:
|
||||
state_helper.state_as_number(state)
|
||||
except ValueError:
|
||||
_LOGGER.debug("Unsupported sensor: %s", state.entity_id)
|
||||
return None
|
||||
return "sensor_state"
|
||||
return f"sensor_unit_{unit}"
|
||||
if unit not in (None, ""):
|
||||
return f"sensor_unit_{unit}"
|
||||
return "sensor_state"
|
||||
|
||||
@staticmethod
|
||||
def _unit_string(unit: str | None) -> str | None:
|
||||
@ -713,11 +697,8 @@ class PrometheusMetrics:
|
||||
"switch_state", prometheus_client.Gauge, "State of the switch (0/1)"
|
||||
)
|
||||
|
||||
try:
|
||||
value = self.state_as_number(state)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self._handle_attributes(state)
|
||||
|
||||
@ -726,11 +707,8 @@ class PrometheusMetrics:
|
||||
"fan_state", prometheus_client.Gauge, "State of the fan (0/1)"
|
||||
)
|
||||
|
||||
try:
|
||||
value = self.state_as_number(state)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
fan_speed_percent = state.attributes.get(ATTR_PERCENTAGE)
|
||||
if fan_speed_percent is not None:
|
||||
@ -796,8 +774,8 @@ class PrometheusMetrics:
|
||||
prometheus_client.Gauge,
|
||||
"Value of counter entities",
|
||||
)
|
||||
|
||||
metric.labels(**self._labels(state)).set(self.state_as_number(state))
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _handle_update(self, state: State) -> None:
|
||||
metric = self._metric(
|
||||
@ -805,8 +783,8 @@ class PrometheusMetrics:
|
||||
prometheus_client.Gauge,
|
||||
"Update state, indicating if an update is available (0/1)",
|
||||
)
|
||||
value = self.state_as_number(state)
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
if (value := self.state_as_number(state)) is not None:
|
||||
metric.labels(**self._labels(state)).set(value)
|
||||
|
||||
def _handle_alarm_control_panel(self, state: State) -> None:
|
||||
current_state = state.state
|
||||
|
@ -642,7 +642,7 @@ async def test_sensor_without_unit(
|
||||
domain="sensor",
|
||||
friendly_name="Text Unit",
|
||||
entity="sensor.text_unit",
|
||||
).withValue(0.0).assert_in_metrics(body)
|
||||
).assert_not_in_metrics(body)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("namespace", [""])
|
||||
@ -716,6 +716,13 @@ async def test_input_number(
|
||||
entity="input_number.target_temperature",
|
||||
).withValue(22.7).assert_in_metrics(body)
|
||||
|
||||
EntityMetric(
|
||||
metric_name="input_number_state_celsius",
|
||||
domain="input_number",
|
||||
friendly_name="Converted temperature",
|
||||
entity="input_number.converted_temperature",
|
||||
).withValue(100).assert_in_metrics(body)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("namespace", [""])
|
||||
async def test_number(
|
||||
@ -2207,6 +2214,17 @@ async def input_number_fixture(
|
||||
set_state_with_entry(hass, input_number_3, 22.7)
|
||||
data["input_number_3"] = input_number_3
|
||||
|
||||
input_number_4 = entity_registry.async_get_or_create(
|
||||
domain=input_number.DOMAIN,
|
||||
platform="test",
|
||||
unique_id="input_number_4",
|
||||
suggested_object_id="converted_temperature",
|
||||
original_name="Converted temperature",
|
||||
unit_of_measurement=UnitOfTemperature.FAHRENHEIT,
|
||||
)
|
||||
set_state_with_entry(hass, input_number_4, 212)
|
||||
data["input_number_4"] = input_number_4
|
||||
|
||||
await hass.async_block_till_done()
|
||||
return data
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user