Add unifi power stats for PDU outlets (#98081)
Adds support for power stats for PDU outlets.
This commit is contained in:
parent
c1b47b88f2
commit
fe1f617a35
2 changed files with 207 additions and 0 deletions
|
@ -12,10 +12,12 @@ from typing import Generic
|
|||
|
||||
from aiounifi.interfaces.api_handlers import ItemEvent
|
||||
from aiounifi.interfaces.clients import Clients
|
||||
from aiounifi.interfaces.outlets import Outlets
|
||||
from aiounifi.interfaces.ports import Ports
|
||||
from aiounifi.interfaces.wlans import Wlans
|
||||
from aiounifi.models.api import ApiItemT
|
||||
from aiounifi.models.client import Client
|
||||
from aiounifi.models.outlet import Outlet
|
||||
from aiounifi.models.port import Port
|
||||
from aiounifi.models.wlan import Wlan
|
||||
|
||||
|
@ -84,6 +86,16 @@ def async_wlan_client_value_fn(controller: UniFiController, wlan: Wlan) -> int:
|
|||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_device_outlet_power_supported_fn(
|
||||
controller: UniFiController, obj_id: str
|
||||
) -> bool:
|
||||
"""Determine if an outlet has the power property."""
|
||||
# At this time, an outlet_caps value of 3 is expected to indicate that the outlet
|
||||
# supports metering
|
||||
return controller.api.outlets[obj_id].caps == 3
|
||||
|
||||
|
||||
@dataclass
|
||||
class UnifiSensorEntityDescriptionMixin(Generic[HandlerT, ApiItemT]):
|
||||
"""Validate and load entities from different UniFi handlers."""
|
||||
|
@ -193,6 +205,25 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
|
|||
unique_id_fn=lambda controller, obj_id: f"wlan_clients-{obj_id}",
|
||||
value_fn=async_wlan_client_value_fn,
|
||||
),
|
||||
UnifiSensorEntityDescription[Outlets, Outlet](
|
||||
key="Outlet power metering",
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
has_entity_name=True,
|
||||
allowed_fn=lambda controller, obj_id: True,
|
||||
api_handler_fn=lambda api: api.outlets,
|
||||
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 outlet: f"{outlet.name} Outlet Power",
|
||||
object_fn=lambda api, obj_id: api.outlets[obj_id],
|
||||
should_poll=True,
|
||||
supported_fn=async_device_outlet_power_supported_fn,
|
||||
unique_id_fn=lambda controller, obj_id: f"outlet_power-{obj_id}",
|
||||
value_fn=lambda _, obj: obj.power if obj.relay_state else "0",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -132,6 +132,152 @@ WLAN = {
|
|||
"x_passphrase": "password",
|
||||
}
|
||||
|
||||
PDU_DEVICE_1 = {
|
||||
"_id": "123456654321abcdef012345",
|
||||
"required_version": "5.28.0",
|
||||
"port_table": [],
|
||||
"license_state": "registered",
|
||||
"lcm_brightness_override": False,
|
||||
"type": "usw",
|
||||
"board_rev": 4,
|
||||
"hw_caps": 136,
|
||||
"reboot_duration": 70,
|
||||
"snmp_contact": "",
|
||||
"config_network": {"type": "dhcp", "bonding_enabled": False},
|
||||
"outlet_table": [
|
||||
{
|
||||
"index": 1,
|
||||
"relay_state": True,
|
||||
"cycle_enabled": False,
|
||||
"name": "USB Outlet 1",
|
||||
"outlet_caps": 1,
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"relay_state": True,
|
||||
"cycle_enabled": False,
|
||||
"name": "Outlet 2",
|
||||
"outlet_caps": 3,
|
||||
"outlet_voltage": "119.644",
|
||||
"outlet_current": "0.935",
|
||||
"outlet_power": "73.827",
|
||||
"outlet_power_factor": "0.659",
|
||||
},
|
||||
],
|
||||
"model": "USPPDUP",
|
||||
"manufacturer_id": 4,
|
||||
"ip": "192.168.1.76",
|
||||
"fw2_caps": 0,
|
||||
"jumboframe_enabled": False,
|
||||
"version": "6.5.59.14777",
|
||||
"unsupported_reason": 0,
|
||||
"adoption_completed": True,
|
||||
"outlet_enabled": True,
|
||||
"stp_version": "rstp",
|
||||
"name": "Dummy USP-PDU-Pro",
|
||||
"fw_caps": 1732968229,
|
||||
"lcm_brightness": 80,
|
||||
"internet": True,
|
||||
"mgmt_network_id": "123456654321abcdef012347",
|
||||
"gateway_mac": "01:02:03:04:05:06",
|
||||
"stp_priority": "32768",
|
||||
"lcm_night_mode_begins": "22:00",
|
||||
"two_phase_adopt": False,
|
||||
"connected_at": 1690626493,
|
||||
"inform_ip": "192.168.1.1",
|
||||
"cfgversion": "ba8f30a5a17aad64",
|
||||
"mac": "01:02:03:04:05:ff",
|
||||
"provisioned_at": 1690989511,
|
||||
"inform_url": "http://192.168.1.1:8080/inform",
|
||||
"upgrade_duration": 100,
|
||||
"ethernet_table": [{"num_port": 1, "name": "eth0", "mac": "01:02:03:04:05:a1"}],
|
||||
"flowctrl_enabled": False,
|
||||
"unsupported": False,
|
||||
"ble_caps": 0,
|
||||
"sys_error_caps": 0,
|
||||
"dot1x_portctrl_enabled": False,
|
||||
"last_uplink": {},
|
||||
"disconnected_at": 1690626452,
|
||||
"architecture": "mips",
|
||||
"x_aes_gcm": True,
|
||||
"has_fan": False,
|
||||
"outlet_overrides": [
|
||||
{
|
||||
"cycle_enabled": False,
|
||||
"name": "USB Outlet 1",
|
||||
"relay_state": True,
|
||||
"index": 1,
|
||||
},
|
||||
{"cycle_enabled": False, "name": "Outlet 2", "relay_state": True, "index": 2},
|
||||
],
|
||||
"model_incompatible": False,
|
||||
"satisfaction": 100,
|
||||
"model_in_eol": False,
|
||||
"anomalies": -1,
|
||||
"has_temperature": False,
|
||||
"switch_caps": {},
|
||||
"adopted_by_client": "web",
|
||||
"snmp_location": "",
|
||||
"model_in_lts": False,
|
||||
"kernel_version": "4.14.115",
|
||||
"serial": "abc123",
|
||||
"power_source_ctrl_enabled": False,
|
||||
"lcm_night_mode_ends": "08:00",
|
||||
"adopted": True,
|
||||
"hash_id": "abcdef123456",
|
||||
"device_id": "mock-pdu",
|
||||
"uplink": {},
|
||||
"state": 1,
|
||||
"start_disconnected_millis": 1690626383386,
|
||||
"credential_caps": 0,
|
||||
"default": False,
|
||||
"discovered_via": "l2",
|
||||
"adopt_ip": "10.0.10.4",
|
||||
"adopt_url": "http://192.168.1.1:8080/inform",
|
||||
"last_seen": 1691518814,
|
||||
"min_inform_interval_seconds": 10,
|
||||
"upgradable": False,
|
||||
"adoptable_when_upgraded": False,
|
||||
"rollupgrade": False,
|
||||
"known_cfgversion": "abcfde03929",
|
||||
"uptime": 1193042,
|
||||
"_uptime": 1193042,
|
||||
"locating": False,
|
||||
"start_connected_millis": 1690626493324,
|
||||
"prev_non_busy_state": 5,
|
||||
"next_interval": 47,
|
||||
"sys_stats": {},
|
||||
"system-stats": {"cpu": "1.4", "mem": "28.9", "uptime": "1193042"},
|
||||
"ssh_session_table": [],
|
||||
"lldp_table": [],
|
||||
"displayable_version": "6.5.59",
|
||||
"connection_network_id": "123456654321abcdef012349",
|
||||
"connection_network_name": "Default",
|
||||
"startup_timestamp": 1690325774,
|
||||
"is_access_point": False,
|
||||
"safe_for_autoupgrade": True,
|
||||
"overheating": False,
|
||||
"power_source": "0",
|
||||
"total_max_power": 0,
|
||||
"outlet_ac_power_budget": "1875.000",
|
||||
"outlet_ac_power_consumption": "201.683",
|
||||
"downlink_table": [],
|
||||
"uplink_depth": 1,
|
||||
"downlink_lldp_macs": [],
|
||||
"dhcp_server_table": [],
|
||||
"connect_request_ip": "10.0.10.4",
|
||||
"connect_request_port": "57951",
|
||||
"ipv4_lease_expiration_timestamp_seconds": 1691576686,
|
||||
"stat": {},
|
||||
"tx_bytes": 1426780,
|
||||
"rx_bytes": 1435064,
|
||||
"bytes": 2861844,
|
||||
"num_sta": 0,
|
||||
"user-num_sta": 0,
|
||||
"guest-num_sta": 0,
|
||||
"x_has_ssh_hostkey": True,
|
||||
}
|
||||
|
||||
|
||||
async def test_no_clients(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
|
||||
|
@ -571,3 +717,33 @@ async def test_wlan_client_sensors(
|
|||
mock_unifi_websocket(message=MessageKey.WLAN_CONF_UPDATED, data=wlan_1)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get("sensor.ssid_1").state == "0"
|
||||
|
||||
|
||||
async def test_outlet_power_readings(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_unifi_websocket
|
||||
) -> None:
|
||||
"""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()) == 5
|
||||
assert len(hass.states.async_entity_ids(SENSOR_DOMAIN)) == 1
|
||||
|
||||
ent_reg = er.async_get(hass)
|
||||
ent_reg_entry = ent_reg.async_get("sensor.dummy_usp_pdu_pro_outlet_2_outlet_power")
|
||||
assert ent_reg_entry.unique_id == "outlet_power-01:02:03:04:05:ff_2"
|
||||
assert ent_reg_entry.entity_category is EntityCategory.DIAGNOSTIC
|
||||
|
||||
outlet_2 = hass.states.get("sensor.dummy_usp_pdu_pro_outlet_2_outlet_power")
|
||||
assert outlet_2.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER
|
||||
assert outlet_2.state == "73.827"
|
||||
|
||||
# Verify state update
|
||||
pdu_device_state_update = deepcopy(PDU_DEVICE_1)
|
||||
|
||||
pdu_device_state_update["outlet_table"][1]["outlet_power"] = "123.45"
|
||||
|
||||
mock_unifi_websocket(message=MessageKey.DEVICE, data=pdu_device_state_update)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
outlet_2 = hass.states.get("sensor.dummy_usp_pdu_pro_outlet_2_outlet_power")
|
||||
assert outlet_2.state == "123.45"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue