Add UniFi device uptime and temperature sensors (#99307)

* Add UniFi device uptime and temperature sensors

* Add native_unit_of_measurement to temperature
Remove seconds and milliseconds from device uptime
This commit is contained in:
Robert Svensson 2023-09-09 11:12:44 +02:00 committed by GitHub
parent f903cd6fc0
commit cf47a6c515
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 4 deletions

View file

@ -27,6 +27,7 @@ from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
UnitOfTemperature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, UnitOfInformation, UnitOfPower
@ -88,6 +89,16 @@ def async_wlan_client_value_fn(controller: UniFiController, wlan: Wlan) -> int:
)
@callback
def async_device_uptime_value_fn(
controller: UniFiController, device: Device
) -> datetime:
"""Calculate the uptime of the device."""
return (dt_util.now() - timedelta(seconds=device.uptime)).replace(
second=0, microsecond=0
)
@callback
def async_device_outlet_power_supported_fn(
controller: UniFiController, obj_id: str
@ -178,7 +189,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
value_fn=lambda _, obj: obj.poe_power if obj.poe_mode != "off" else "0",
),
UnifiSensorEntityDescription[Clients, Client](
key="Uptime sensor",
key="Client uptime",
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
has_entity_name=True,
@ -272,6 +283,43 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
unique_id_fn=lambda controller, obj_id: f"ac_power_conumption-{obj_id}",
value_fn=lambda controller, device: device.outlet_ac_power_consumption,
),
UnifiSensorEntityDescription[Devices, Device](
key="Device uptime",
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
has_entity_name=True,
allowed_fn=lambda controller, obj_id: True,
api_handler_fn=lambda api: api.devices,
available_fn=async_device_available_fn,
device_info_fn=async_device_device_info_fn,
event_is_on=None,
event_to_subscribe=None,
name_fn=lambda device: "Uptime",
object_fn=lambda api, obj_id: api.devices[obj_id],
should_poll=False,
supported_fn=lambda controller, obj_id: True,
unique_id_fn=lambda controller, obj_id: f"device_uptime-{obj_id}",
value_fn=async_device_uptime_value_fn,
),
UnifiSensorEntityDescription[Devices, Device](
key="Device temperature",
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
has_entity_name=True,
allowed_fn=lambda controller, obj_id: True,
api_handler_fn=lambda api: api.devices,
available_fn=async_device_available_fn,
device_info_fn=async_device_device_info_fn,
event_is_on=None,
event_to_subscribe=None,
name_fn=lambda device: "Temperature",
object_fn=lambda api, obj_id: api.devices[obj_id],
should_poll=False,
supported_fn=lambda ctrlr, obj_id: ctrlr.api.devices[obj_id].has_temperature,
unique_id_fn=lambda controller, obj_id: f"device_temperature-{obj_id}",
value_fn=lambda ctrlr, device: device.general_temperature,
),
)

View file

@ -566,7 +566,7 @@ async def test_poe_port_switches(
) -> None:
"""Test the update_items function with some clients."""
await setup_unifi_integration(hass, aioclient_mock, devices_response=[DEVICE_1])
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 0
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
ent_reg = er.async_get(hass)
ent_reg_entry = ent_reg.async_get("sensor.mock_name_port_1_poe_power")
@ -788,8 +788,8 @@ async def test_outlet_power_readings(
"""Test the outlet power reporting on PDU devices."""
await setup_unifi_integration(hass, aioclient_mock, devices_response=[PDU_DEVICE_1])
assert len(hass.states.async_all()) == 9
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 3
assert len(hass.states.async_all()) == 10
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4
ent_reg = er.async_get(hass)
ent_reg_entry = ent_reg.async_get(f"sensor.{entity_id}")
@ -809,3 +809,104 @@ async def test_outlet_power_readings(
sensor_data = hass.states.get(f"sensor.{entity_id}")
assert sensor_data.state == expected_update_value
async def test_device_uptime(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_unifi_websocket
) -> None:
"""Verify that uptime sensors are working as expected."""
device = {
"board_rev": 3,
"device_id": "mock-id",
"has_fan": True,
"fan_level": 0,
"ip": "10.0.1.1",
"last_seen": 1562600145,
"mac": "00:00:00:00:01:01",
"model": "US16P150",
"name": "Device",
"next_interval": 20,
"overheating": True,
"state": 1,
"type": "usw",
"upgradable": True,
"uptime": 60,
"version": "4.0.42.10433",
}
now = datetime(2021, 1, 1, 1, 1, 0, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.now", return_value=now):
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
assert hass.states.get("sensor.device_uptime").state == "2021-01-01T01:00:00+00:00"
ent_reg = er.async_get(hass)
assert (
ent_reg.async_get("sensor.device_uptime").entity_category
is EntityCategory.DIAGNOSTIC
)
# Verify normal new event doesn't change uptime
# 4 seconds has passed
device["uptime"] = 64
now = datetime(2021, 1, 1, 1, 1, 4, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.now", return_value=now):
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
await hass.async_block_till_done()
assert hass.states.get("sensor.device_uptime").state == "2021-01-01T01:00:00+00:00"
# Verify new event change uptime
# 1 month has passed
device["uptime"] = 60
now = datetime(2021, 2, 1, 1, 1, 0, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.now", return_value=now):
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
await hass.async_block_till_done()
assert hass.states.get("sensor.device_uptime").state == "2021-02-01T01:00:00+00:00"
async def test_device_temperature(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_unifi_websocket
) -> None:
"""Verify that temperature sensors are working as expected."""
device = {
"board_rev": 3,
"device_id": "mock-id",
"general_temperature": 30,
"has_fan": True,
"has_temperature": True,
"fan_level": 0,
"ip": "10.0.1.1",
"last_seen": 1562600145,
"mac": "00:00:00:00:01:01",
"model": "US16P150",
"name": "Device",
"next_interval": 20,
"overheating": True,
"state": 1,
"type": "usw",
"upgradable": True,
"uptime": 60,
"version": "4.0.42.10433",
}
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
assert hass.states.get("sensor.device_temperature").state == "30"
ent_reg = er.async_get(hass)
assert (
ent_reg.async_get("sensor.device_temperature").entity_category
is EntityCategory.DIAGNOSTIC
)
# Verify new event change temperature
device["general_temperature"] = 60
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
await hass.async_block_till_done()
assert hass.states.get("sensor.device_temperature").state == "60"