Add sensor for CPU and memory utilization for unifi device (#114986)

Add system stats to unifi device sensors
This commit is contained in:
Kim de Vos 2024-04-08 19:29:54 +02:00 committed by GitHub
parent f9a7e6bb9f
commit f23e48f373
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 86 additions and 3 deletions

View file

@ -10,6 +10,7 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from decimal import Decimal from decimal import Decimal
from functools import partial
from aiounifi.interfaces.api_handlers import ItemEvent from aiounifi.interfaces.api_handlers import ItemEvent
from aiounifi.interfaces.clients import Clients from aiounifi.interfaces.clients import Clients
@ -32,7 +33,7 @@ from homeassistant.components.sensor import (
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, UnitOfDataRate, UnitOfPower from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfDataRate, UnitOfPower
from homeassistant.core import Event as core_Event, HomeAssistant, callback from homeassistant.core import Event as core_Event, HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -140,6 +141,16 @@ def async_device_outlet_supported_fn(hub: UnifiHub, obj_id: str) -> bool:
return hub.api.devices[obj_id].outlet_ac_power_budget is not None return hub.api.devices[obj_id].outlet_ac_power_budget is not None
def device_system_stats_supported_fn(
stat_index: int, hub: UnifiHub, obj_id: str
) -> bool:
"""Determine if a device supports reading item at index in system stats."""
return (
"system-stats" in hub.api.devices[obj_id].raw
and hub.api.devices[obj_id].system_stats[stat_index] != ""
)
@callback @callback
def async_client_is_connected_fn(hub: UnifiHub, obj_id: str) -> bool: def async_client_is_connected_fn(hub: UnifiHub, obj_id: str) -> bool:
"""Check if client was last seen recently.""" """Check if client was last seen recently."""
@ -352,6 +363,34 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
unique_id_fn=lambda hub, obj_id: f"password-{obj_id}", unique_id_fn=lambda hub, obj_id: f"password-{obj_id}",
value_fn=lambda hub, obj: obj.x_passphrase, value_fn=lambda hub, obj: obj.x_passphrase,
), ),
UnifiSensorEntityDescription[Devices, Device](
key="Device CPU utilization",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
api_handler_fn=lambda api: api.devices,
available_fn=async_device_available_fn,
device_info_fn=async_device_device_info_fn,
name_fn=lambda device: "CPU utilization",
object_fn=lambda api, obj_id: api.devices[obj_id],
supported_fn=partial(device_system_stats_supported_fn, 0),
unique_id_fn=lambda hub, obj_id: f"cpu_utilization-{obj_id}",
value_fn=lambda hub, device: device.system_stats[0],
),
UnifiSensorEntityDescription[Devices, Device](
key="Device memory utilization",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
api_handler_fn=lambda api: api.devices,
available_fn=async_device_available_fn,
device_info_fn=async_device_device_info_fn,
name_fn=lambda device: "Memory utilization",
object_fn=lambda api, obj_id: api.devices[obj_id],
supported_fn=partial(device_system_stats_supported_fn, 1),
unique_id_fn=lambda hub, obj_id: f"memory_utilization-{obj_id}",
value_fn=lambda hub, device: device.system_stats[1],
),
) )

View file

@ -835,8 +835,8 @@ async def test_outlet_power_readings(
"""Test the outlet power reporting on PDU devices.""" """Test the outlet power reporting on PDU devices."""
await setup_unifi_integration(hass, aioclient_mock, devices_response=[PDU_DEVICE_1]) await setup_unifi_integration(hass, aioclient_mock, devices_response=[PDU_DEVICE_1])
assert len(hass.states.async_all()) == 11 assert len(hass.states.async_all()) == 13
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 5 assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 7
ent_reg_entry = entity_registry.async_get(f"sensor.{entity_id}") ent_reg_entry = entity_registry.async_get(f"sensor.{entity_id}")
assert ent_reg_entry.unique_id == expected_unique_id assert ent_reg_entry.unique_id == expected_unique_id
@ -1069,3 +1069,47 @@ async def test_wlan_password(
mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=wlan_1) mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=wlan_1)
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get(sensor_password).state == password assert hass.states.get(sensor_password).state == password
async def test_device_system_stats(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
aioclient_mock: AiohttpClientMocker,
mock_unifi_websocket,
) -> None:
"""Verify that device stats sensors are working as expected."""
device = {
"device_id": "mock-id",
"mac": "00:00:00:00:01:01",
"model": "US16P150",
"name": "Device",
"state": 1,
"version": "4.0.42.10433",
"system-stats": {"cpu": 5.8, "mem": 31.1, "uptime": 7316},
}
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
assert len(hass.states.async_all()) == 8
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4
assert hass.states.get("sensor.device_cpu_utilization").state == "5.8"
assert hass.states.get("sensor.device_memory_utilization").state == "31.1"
assert (
entity_registry.async_get("sensor.device_cpu_utilization").entity_category
is EntityCategory.DIAGNOSTIC
)
assert (
entity_registry.async_get("sensor.device_memory_utilization").entity_category
is EntityCategory.DIAGNOSTIC
)
# Verify new event change system-stats
device["system-stats"] = {"cpu": 7.7, "mem": 33.3, "uptime": 7316}
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
assert hass.states.get("sensor.device_cpu_utilization").state == "7.7"
assert hass.states.get("sensor.device_memory_utilization").state == "33.3"