mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Add Unifi device state for additional diagnostics (#105138)
* Add device state for additional diagnostics * Add state test and fix existing tests * Utilize IntEnum and dict for state lookup * Update aiounifi to v68
This commit is contained in:
parent
9c134c6b51
commit
1271f16385
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from aiounifi.models.device import DeviceState
|
||||||
|
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__package__)
|
LOGGER = logging.getLogger(__package__)
|
||||||
@ -46,3 +48,19 @@ ATTR_MANUFACTURER = "Ubiquiti Networks"
|
|||||||
BLOCK_SWITCH = "block"
|
BLOCK_SWITCH = "block"
|
||||||
DPI_SWITCH = "dpi"
|
DPI_SWITCH = "dpi"
|
||||||
OUTLET_SWITCH = "outlet"
|
OUTLET_SWITCH = "outlet"
|
||||||
|
|
||||||
|
DEVICE_STATES = {
|
||||||
|
DeviceState.DISCONNECTED: "Disconnected",
|
||||||
|
DeviceState.CONNECTED: "Connected",
|
||||||
|
DeviceState.PENDING: "Pending",
|
||||||
|
DeviceState.FIRMWARE_MISMATCH: "Firmware Mismatch",
|
||||||
|
DeviceState.UPGRADING: "Upgrading",
|
||||||
|
DeviceState.PROVISIONING: "Provisioning",
|
||||||
|
DeviceState.HEARTBEAT_MISSED: "Heartbeat Missed",
|
||||||
|
DeviceState.ADOPTING: "Adopting",
|
||||||
|
DeviceState.DELETING: "Deleting",
|
||||||
|
DeviceState.INFORM_ERROR: "Inform Error",
|
||||||
|
DeviceState.ADOPTION_FALIED: "Adoption Failed",
|
||||||
|
DeviceState.ISOLATED: "Isolated",
|
||||||
|
DeviceState.UNKNOWN: "Unknown",
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"loggers": ["aiounifi"],
|
"loggers": ["aiounifi"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"requirements": ["aiounifi==67"],
|
"requirements": ["aiounifi==68"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"manufacturer": "Ubiquiti Networks",
|
"manufacturer": "Ubiquiti Networks",
|
||||||
|
@ -36,6 +36,7 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
|
from .const import DEVICE_STATES
|
||||||
from .controller import UniFiController
|
from .controller import UniFiController
|
||||||
from .entity import (
|
from .entity import (
|
||||||
HandlerT,
|
HandlerT,
|
||||||
@ -138,6 +139,12 @@ class UnifiSensorEntityDescriptionMixin(Generic[HandlerT, ApiItemT]):
|
|||||||
value_fn: Callable[[UniFiController, ApiItemT], datetime | float | str | None]
|
value_fn: Callable[[UniFiController, ApiItemT], datetime | float | str | None]
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_device_state_value_fn(controller: UniFiController, device: Device) -> str:
|
||||||
|
"""Retrieve the state of the device."""
|
||||||
|
return DEVICE_STATES[device.state]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class UnifiSensorEntityDescription(
|
class UnifiSensorEntityDescription(
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
@ -343,6 +350,25 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
|
|||||||
unique_id_fn=lambda controller, obj_id: f"device_temperature-{obj_id}",
|
unique_id_fn=lambda controller, obj_id: f"device_temperature-{obj_id}",
|
||||||
value_fn=lambda ctrlr, device: device.general_temperature,
|
value_fn=lambda ctrlr, device: device.general_temperature,
|
||||||
),
|
),
|
||||||
|
UnifiSensorEntityDescription[Devices, Device](
|
||||||
|
key="Device State",
|
||||||
|
device_class=SensorDeviceClass.ENUM,
|
||||||
|
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: "State",
|
||||||
|
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_state-{obj_id}",
|
||||||
|
value_fn=async_device_state_value_fn,
|
||||||
|
options=list(DEVICE_STATES.values()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ aiosyncthing==0.5.1
|
|||||||
aiotractive==0.5.6
|
aiotractive==0.5.6
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==67
|
aiounifi==68
|
||||||
|
|
||||||
# homeassistant.components.vlc_telnet
|
# homeassistant.components.vlc_telnet
|
||||||
aiovlc==0.1.0
|
aiovlc==0.1.0
|
||||||
|
@ -350,7 +350,7 @@ aiosyncthing==0.5.1
|
|||||||
aiotractive==0.5.6
|
aiotractive==0.5.6
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==67
|
aiounifi==68
|
||||||
|
|
||||||
# homeassistant.components.vlc_telnet
|
# homeassistant.components.vlc_telnet
|
||||||
aiovlc==0.1.0
|
aiovlc==0.1.0
|
||||||
|
@ -3,6 +3,7 @@ from copy import deepcopy
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from aiounifi.models.device import DeviceState
|
||||||
from aiounifi.models.message import MessageKey
|
from aiounifi.models.message import MessageKey
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
@ -20,6 +21,7 @@ from homeassistant.components.unifi.const import (
|
|||||||
CONF_ALLOW_UPTIME_SENSORS,
|
CONF_ALLOW_UPTIME_SENSORS,
|
||||||
CONF_TRACK_CLIENTS,
|
CONF_TRACK_CLIENTS,
|
||||||
CONF_TRACK_DEVICES,
|
CONF_TRACK_DEVICES,
|
||||||
|
DEVICE_STATES,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
|
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
|
||||||
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_UNAVAILABLE, EntityCategory
|
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_UNAVAILABLE, EntityCategory
|
||||||
@ -584,7 +586,7 @@ async def test_poe_port_switches(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test the update_items function with some clients."""
|
"""Test the update_items function with some clients."""
|
||||||
await setup_unifi_integration(hass, aioclient_mock, devices_response=[DEVICE_1])
|
await setup_unifi_integration(hass, aioclient_mock, devices_response=[DEVICE_1])
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
ent_reg = er.async_get(hass)
|
||||||
ent_reg_entry = ent_reg.async_get("sensor.mock_name_port_1_poe_power")
|
ent_reg_entry = ent_reg.async_get("sensor.mock_name_port_1_poe_power")
|
||||||
@ -807,8 +809,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()) == 10
|
assert len(hass.states.async_all()) == 11
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 4
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 5
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
ent_reg = er.async_get(hass)
|
||||||
ent_reg_entry = ent_reg.async_get(f"sensor.{entity_id}")
|
ent_reg_entry = ent_reg.async_get(f"sensor.{entity_id}")
|
||||||
@ -856,7 +858,7 @@ async def test_device_uptime(
|
|||||||
now = datetime(2021, 1, 1, 1, 1, 0, tzinfo=dt_util.UTC)
|
now = datetime(2021, 1, 1, 1, 1, 0, tzinfo=dt_util.UTC)
|
||||||
with patch("homeassistant.util.dt.now", return_value=now):
|
with patch("homeassistant.util.dt.now", return_value=now):
|
||||||
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
|
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
|
||||||
assert hass.states.get("sensor.device_uptime").state == "2021-01-01T01:00:00+00:00"
|
assert hass.states.get("sensor.device_uptime").state == "2021-01-01T01:00:00+00:00"
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
ent_reg = er.async_get(hass)
|
||||||
@ -912,7 +914,7 @@ async def test_device_temperature(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
|
await setup_unifi_integration(hass, aioclient_mock, devices_response=[device])
|
||||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 2
|
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 3
|
||||||
assert hass.states.get("sensor.device_temperature").state == "30"
|
assert hass.states.get("sensor.device_temperature").state == "30"
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
ent_reg = er.async_get(hass)
|
||||||
@ -925,3 +927,43 @@ async def test_device_temperature(
|
|||||||
device["general_temperature"] = 60
|
device["general_temperature"] = 60
|
||||||
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
|
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
|
||||||
assert hass.states.get("sensor.device_temperature").state == "60"
|
assert hass.states.get("sensor.device_temperature").state == "60"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_state(
|
||||||
|
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_unifi_websocket
|
||||||
|
) -> None:
|
||||||
|
"""Verify that state 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)) == 3
|
||||||
|
|
||||||
|
ent_reg = er.async_get(hass)
|
||||||
|
assert (
|
||||||
|
ent_reg.async_get("sensor.device_state").entity_category
|
||||||
|
is EntityCategory.DIAGNOSTIC
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in list(map(int, DeviceState)):
|
||||||
|
device["state"] = i
|
||||||
|
mock_unifi_websocket(message=MessageKey.DEVICE, data=device)
|
||||||
|
assert hass.states.get("sensor.device_state").state == DEVICE_STATES[i]
|
||||||
|
@ -117,7 +117,7 @@ async def test_device_updates(
|
|||||||
|
|
||||||
# Simulate update finished
|
# Simulate update finished
|
||||||
|
|
||||||
device_1["state"] = "0"
|
device_1["state"] = 0
|
||||||
device_1["version"] = "4.3.17.11279"
|
device_1["version"] = "4.3.17.11279"
|
||||||
device_1["upgradable"] = False
|
device_1["upgradable"] = False
|
||||||
del device_1["upgrade_to_firmware"]
|
del device_1["upgrade_to_firmware"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user