Fix UniFi device tracker on controllers only reporting events (#72240)

Add events to UniFi client tracker in case controller does not report proper data
pull/72435/head
Robert Svensson 2022-05-24 16:06:30 +02:00 committed by GitHub
parent 1113d9bea9
commit dc0d065901
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 160 additions and 2 deletions

View File

@ -3,7 +3,7 @@
from datetime import timedelta
import logging
from aiounifi.api import SOURCE_DATA
from aiounifi.api import SOURCE_DATA, SOURCE_EVENT
from aiounifi.events import (
ACCESS_POINT_UPGRADED,
GATEWAY_UPGRADED,
@ -156,6 +156,8 @@ class UniFiClientTracker(UniFiClient, ScannerEntity):
self._controller_connection_state_changed = False
self._only_listen_to_data_source = False
last_seen = client.last_seen or 0
self.schedule_update = self._is_connected = (
self.is_wired == client.is_wired
@ -224,6 +226,23 @@ class UniFiClientTracker(UniFiClient, ScannerEntity):
):
self._is_connected = True
self.schedule_update = True
self._only_listen_to_data_source = True
elif (
self.client.last_updated == SOURCE_EVENT
and not self._only_listen_to_data_source
):
if (self.is_wired and self.client.event.event in WIRED_CONNECTION) or (
not self.is_wired and self.client.event.event in WIRELESS_CONNECTION
):
self._is_connected = True
self.schedule_update = False
self.controller.async_heartbeat(self.unique_id)
super().async_update_callback()
else:
self.schedule_update = True
self._async_log_debug_data("update_callback")

View File

@ -3,7 +3,12 @@
from datetime import timedelta
from unittest.mock import patch
from aiounifi.controller import MESSAGE_CLIENT, MESSAGE_CLIENT_REMOVED, MESSAGE_DEVICE
from aiounifi.controller import (
MESSAGE_CLIENT,
MESSAGE_CLIENT_REMOVED,
MESSAGE_DEVICE,
MESSAGE_EVENT,
)
from aiounifi.websocket import STATE_DISCONNECTED, STATE_RUNNING
from homeassistant import config_entries
@ -169,6 +174,140 @@ async def test_tracked_clients(
assert hass.states.get("device_tracker.client_1").state == STATE_HOME
async def test_tracked_wireless_clients_event_source(
hass, aioclient_mock, mock_unifi_websocket, mock_device_registry
):
"""Verify tracking of wireless clients based on event source."""
client = {
"ap_mac": "00:00:00:00:02:01",
"essid": "ssid",
"hostname": "client",
"ip": "10.0.0.1",
"is_wired": False,
"last_seen": 1562600145,
"mac": "00:00:00:00:00:01",
}
config_entry = await setup_unifi_integration(
hass, aioclient_mock, clients_response=[client]
)
controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id]
assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 1
assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME
# State change signalling works with events
# Connected event
event = {
"user": client["mac"],
"ssid": client["essid"],
"ap": client["ap_mac"],
"radio": "na",
"channel": "44",
"hostname": client["hostname"],
"key": "EVT_WU_Connected",
"subsystem": "wlan",
"site_id": "name",
"time": 1587753456179,
"datetime": "2020-04-24T18:37:36Z",
"msg": f'User{[client["mac"]]} has connected to AP[{client["ap_mac"]}] with SSID "{client["essid"]}" on "channel 44(na)"',
"_id": "5ea331fa30c49e00f90ddc1a",
}
mock_unifi_websocket(
data={
"meta": {"message": MESSAGE_EVENT},
"data": [event],
}
)
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_HOME
# Disconnected event
event = {
"user": client["mac"],
"ssid": client["essid"],
"hostname": client["hostname"],
"ap": client["ap_mac"],
"duration": 467,
"bytes": 459039,
"key": "EVT_WU_Disconnected",
"subsystem": "wlan",
"site_id": "name",
"time": 1587752927000,
"datetime": "2020-04-24T18:28:47Z",
"msg": f'User{[client["mac"]]} disconnected from "{client["essid"]}" (7m 47s connected, 448.28K bytes, last AP[{client["ap_mac"]}])',
"_id": "5ea32ff730c49e00f90dca1a",
}
mock_unifi_websocket(
data={
"meta": {"message": MESSAGE_EVENT},
"data": [event],
}
)
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_HOME
# Change time to mark client as away
new_time = dt_util.utcnow() + controller.option_detection_time
with patch("homeassistant.util.dt.utcnow", return_value=new_time):
async_fire_time_changed(hass, new_time)
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_NOT_HOME
# To limit false positives in client tracker
# data sources are prioritized when available
# once real data is received events will be ignored.
# New data
mock_unifi_websocket(
data={
"meta": {"message": MESSAGE_CLIENT},
"data": [client],
}
)
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_HOME
# Disconnection event will be ignored
event = {
"user": client["mac"],
"ssid": client["essid"],
"hostname": client["hostname"],
"ap": client["ap_mac"],
"duration": 467,
"bytes": 459039,
"key": "EVT_WU_Disconnected",
"subsystem": "wlan",
"site_id": "name",
"time": 1587752927000,
"datetime": "2020-04-24T18:28:47Z",
"msg": f'User{[client["mac"]]} disconnected from "{client["essid"]}" (7m 47s connected, 448.28K bytes, last AP[{client["ap_mac"]}])',
"_id": "5ea32ff730c49e00f90dca1a",
}
mock_unifi_websocket(
data={
"meta": {"message": MESSAGE_EVENT},
"data": [event],
}
)
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_HOME
# Change time to mark client as away
new_time = dt_util.utcnow() + controller.option_detection_time
with patch("homeassistant.util.dt.utcnow", return_value=new_time):
async_fire_time_changed(hass, new_time)
await hass.async_block_till_done()
assert hass.states.get("device_tracker.client").state == STATE_HOME
async def test_tracked_devices(
hass, aioclient_mock, mock_unifi_websocket, mock_device_registry
):