mirror of
https://github.com/home-assistant/core.git
synced 2025-07-13 00:07:10 +00:00
Add generic unavailable and last_updated metrics for prometheus (#37456)
* Add generic unavailable and last_updated metrics for prometheus * Updated with feedback from the code review
This commit is contained in:
parent
c6ab2c5d0a
commit
16a947aa5f
@ -25,6 +25,7 @@ from homeassistant.const import (
|
||||
CONTENT_TYPE_TEXT_PLAIN,
|
||||
EVENT_STATE_CHANGED,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT,
|
||||
UNIT_PERCENTAGE,
|
||||
@ -153,13 +154,28 @@ class PrometheusMetrics:
|
||||
|
||||
handler = f"_handle_{domain}"
|
||||
|
||||
if hasattr(self, handler):
|
||||
if hasattr(self, handler) and state.state != STATE_UNAVAILABLE:
|
||||
getattr(self, handler)(state)
|
||||
|
||||
metric = self._metric(
|
||||
labels = self._labels(state)
|
||||
state_change = self._metric(
|
||||
"state_change", self.prometheus_cli.Counter, "The number of state changes"
|
||||
)
|
||||
metric.labels(**self._labels(state)).inc()
|
||||
state_change.labels(**labels).inc()
|
||||
|
||||
entity_available = self._metric(
|
||||
"entity_available",
|
||||
self.prometheus_cli.Gauge,
|
||||
"Entity is available (not in the unavailable state)",
|
||||
)
|
||||
entity_available.labels(**labels).set(float(state.state != STATE_UNAVAILABLE))
|
||||
|
||||
last_updated_time_seconds = self._metric(
|
||||
"last_updated_time_seconds",
|
||||
self.prometheus_cli.Gauge,
|
||||
"The last_updated timestamp",
|
||||
)
|
||||
last_updated_time_seconds.labels(**labels).set(state.last_updated.timestamp())
|
||||
|
||||
def _handle_attributes(self, state):
|
||||
for key, value in state.attributes.items():
|
||||
|
@ -1,9 +1,9 @@
|
||||
"""The tests for the Prometheus exporter."""
|
||||
from dataclasses import dataclass
|
||||
import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components import climate, humidifier, sensor
|
||||
from homeassistant.components.demo.sensor import DemoSensor
|
||||
import homeassistant.components.prometheus as prometheus
|
||||
@ -16,6 +16,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import split_entity_id
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
import tests.async_mock as mock
|
||||
|
||||
@ -30,21 +31,18 @@ class FilterTest:
|
||||
should_pass: bool
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def prometheus_client(loop, hass, hass_client):
|
||||
async def prometheus_client(hass, hass_client):
|
||||
"""Initialize an hass_client with Prometheus component."""
|
||||
await async_setup_component(hass, prometheus.DOMAIN, {prometheus.DOMAIN: {}})
|
||||
|
||||
await setup.async_setup_component(
|
||||
hass, sensor.DOMAIN, {"sensor": [{"platform": "demo"}]}
|
||||
)
|
||||
await async_setup_component(hass, sensor.DOMAIN, {"sensor": [{"platform": "demo"}]})
|
||||
|
||||
await setup.async_setup_component(
|
||||
await async_setup_component(
|
||||
hass, climate.DOMAIN, {"climate": [{"platform": "demo"}]}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await setup.async_setup_component(
|
||||
await async_setup_component(
|
||||
hass, humidifier.DOMAIN, {"humidifier": [{"platform": "demo"}]}
|
||||
)
|
||||
|
||||
@ -60,7 +58,11 @@ async def prometheus_client(loop, hass, hass_client):
|
||||
)
|
||||
sensor2.hass = hass
|
||||
sensor2.entity_id = "sensor.radio_energy"
|
||||
await sensor2.async_update_ha_state()
|
||||
with mock.patch(
|
||||
"homeassistant.util.dt.utcnow",
|
||||
return_value=datetime.datetime(1970, 1, 2, tzinfo=dt_util.UTC),
|
||||
):
|
||||
await sensor2.async_update_ha_state()
|
||||
|
||||
sensor3 = DemoSensor(
|
||||
None, "Electricity price", 0.123, None, f"SEK/{ENERGY_KILO_WATT_HOUR}", None
|
||||
@ -89,9 +91,10 @@ async def prometheus_client(loop, hass, hass_client):
|
||||
return await hass_client()
|
||||
|
||||
|
||||
async def test_view(prometheus_client): # pylint: disable=redefined-outer-name
|
||||
async def test_view(hass, hass_client):
|
||||
"""Test prometheus metrics view."""
|
||||
resp = await prometheus_client.get(prometheus.API_ENDPOINT)
|
||||
client = await prometheus_client(hass, hass_client)
|
||||
resp = await client.get(prometheus.API_ENDPOINT)
|
||||
|
||||
assert resp.status == 200
|
||||
assert resp.headers["content-type"] == "text/plain"
|
||||
@ -167,6 +170,18 @@ async def test_view(prometheus_client): # pylint: disable=redefined-outer-name
|
||||
'friendly_name="Radio Energy"} 14.0' in body
|
||||
)
|
||||
|
||||
assert (
|
||||
'entity_available{domain="sensor",'
|
||||
'entity="sensor.radio_energy",'
|
||||
'friendly_name="Radio Energy"} 1.0' in body
|
||||
)
|
||||
|
||||
assert (
|
||||
'last_updated_time_seconds{domain="sensor",'
|
||||
'entity="sensor.radio_energy",'
|
||||
'friendly_name="Radio Energy"} 86400.0' in body
|
||||
)
|
||||
|
||||
assert (
|
||||
'sensor_unit_sek_per_kwh{domain="sensor",'
|
||||
'entity="sensor.electricity_price",'
|
||||
|
Loading…
x
Reference in New Issue
Block a user