Add prometheus fan handler (#119805)

Co-authored-by: Anton Tolchanov <1687799+knyar@users.noreply.github.com>
This commit is contained in:
Elliott Balsley 2024-07-07 07:32:15 -07:00 committed by GitHub
parent e2141dc208
commit 275a7499b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 161 additions and 0 deletions

View File

@ -26,6 +26,15 @@ from homeassistant.components.cover import (
ATTR_CURRENT_POSITION,
ATTR_CURRENT_TILT_POSITION,
)
from homeassistant.components.fan import (
ATTR_DIRECTION,
ATTR_OSCILLATING,
ATTR_PERCENTAGE,
ATTR_PRESET_MODE,
ATTR_PRESET_MODES,
DIRECTION_FORWARD,
DIRECTION_REVERSE,
)
from homeassistant.components.http import KEY_HASS, HomeAssistantView
from homeassistant.components.humidifier import ATTR_AVAILABLE_MODES, ATTR_HUMIDITY
from homeassistant.components.light import ATTR_BRIGHTNESS
@ -684,6 +693,63 @@ class PrometheusMetrics:
self._handle_attributes(state)
def _handle_fan(self, state: State) -> None:
metric = self._metric(
"fan_state", prometheus_client.Gauge, "State of the fan (0/1)"
)
try:
value = self.state_as_number(state)
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:
fan_speed_metric = self._metric(
"fan_speed_percent",
prometheus_client.Gauge,
"Fan speed percent (0-100)",
)
fan_speed_metric.labels(**self._labels(state)).set(float(fan_speed_percent))
fan_is_oscillating = state.attributes.get(ATTR_OSCILLATING)
if fan_is_oscillating is not None:
fan_oscillating_metric = self._metric(
"fan_is_oscillating",
prometheus_client.Gauge,
"Whether the fan is oscillating (0/1)",
)
fan_oscillating_metric.labels(**self._labels(state)).set(
float(fan_is_oscillating)
)
fan_preset_mode = state.attributes.get(ATTR_PRESET_MODE)
available_modes = state.attributes.get(ATTR_PRESET_MODES)
if fan_preset_mode and available_modes:
fan_preset_metric = self._metric(
"fan_preset_mode",
prometheus_client.Gauge,
"Fan preset mode enum",
["mode"],
)
for mode in available_modes:
fan_preset_metric.labels(**dict(self._labels(state), mode=mode)).set(
float(mode == fan_preset_mode)
)
fan_direction = state.attributes.get(ATTR_DIRECTION)
if fan_direction is not None:
fan_direction_metric = self._metric(
"fan_direction_reversed",
prometheus_client.Gauge,
"Fan direction reversed (bool)",
)
if fan_direction == DIRECTION_FORWARD:
fan_direction_metric.labels(**self._labels(state)).set(0)
elif fan_direction == DIRECTION_REVERSE:
fan_direction_metric.labels(**self._labels(state)).set(1)
def _handle_zwave(self, state: State) -> None:
self._battery(state)

View File

@ -16,6 +16,7 @@ from homeassistant.components import (
counter,
cover,
device_tracker,
fan,
humidifier,
input_boolean,
input_number,
@ -35,6 +36,15 @@ from homeassistant.components.climate import (
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
)
from homeassistant.components.fan import (
ATTR_DIRECTION,
ATTR_OSCILLATING,
ATTR_PERCENTAGE,
ATTR_PRESET_MODE,
ATTR_PRESET_MODES,
DIRECTION_FORWARD,
DIRECTION_REVERSE,
)
from homeassistant.components.humidifier import ATTR_AVAILABLE_MODES
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
@ -562,6 +572,51 @@ async def test_lock(
)
@pytest.mark.parametrize("namespace", [""])
async def test_fan(
client: ClientSessionGenerator, fan_entities: dict[str, er.RegistryEntry]
) -> None:
"""Test prometheus metrics for fan."""
body = await generate_latest_metrics(client)
assert (
'fan_state{domain="fan",'
'entity="fan.fan_1",'
'friendly_name="Fan 1"} 1.0' in body
)
assert (
'fan_speed_percent{domain="fan",'
'entity="fan.fan_1",'
'friendly_name="Fan 1"} 33.0' in body
)
assert (
'fan_is_oscillating{domain="fan",'
'entity="fan.fan_1",'
'friendly_name="Fan 1"} 1.0' in body
)
assert (
'fan_direction_reversed{domain="fan",'
'entity="fan.fan_1",'
'friendly_name="Fan 1"} 0.0' in body
)
assert (
'fan_preset_mode{domain="fan",'
'entity="fan.fan_1",'
'friendly_name="Fan 1",'
'mode="LO"} 1.0' in body
)
assert (
'fan_direction_reversed{domain="fan",'
'entity="fan.fan_2",'
'friendly_name="Reverse Fan"} 1.0' in body
)
@pytest.mark.parametrize("namespace", [""])
async def test_cover(
client: ClientSessionGenerator, cover_entities: dict[str, er.RegistryEntry]
@ -1788,6 +1843,46 @@ async def switch_fixture(
return data
@pytest.fixture(name="fan_entities")
async def fan_fixture(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> dict[str, er.RegistryEntry]:
"""Simulate fan entities."""
data = {}
fan_1 = entity_registry.async_get_or_create(
domain=fan.DOMAIN,
platform="test",
unique_id="fan_1",
suggested_object_id="fan_1",
original_name="Fan 1",
)
fan_1_attributes = {
ATTR_DIRECTION: DIRECTION_FORWARD,
ATTR_OSCILLATING: True,
ATTR_PERCENTAGE: 33,
ATTR_PRESET_MODE: "LO",
ATTR_PRESET_MODES: ["LO", "OFF", "HI"],
}
set_state_with_entry(hass, fan_1, STATE_ON, fan_1_attributes)
data["fan_1"] = fan_1
data["fan_1_attributes"] = fan_1_attributes
fan_2 = entity_registry.async_get_or_create(
domain=fan.DOMAIN,
platform="test",
unique_id="fan_2",
suggested_object_id="fan_2",
original_name="Reverse Fan",
)
fan_2_attributes = {ATTR_DIRECTION: DIRECTION_REVERSE}
set_state_with_entry(hass, fan_2, STATE_ON, fan_2_attributes)
data["fan_2"] = fan_2
data["fan_2_attributes"] = fan_2_attributes
await hass.async_block_till_done()
return data
@pytest.fixture(name="person_entities")
async def person_fixture(
hass: HomeAssistant, entity_registry: er.EntityRegistry