Skip to content

Commit

Permalink
Fix bug where offline entities are shown with "Restored" warning
Browse files Browse the repository at this point in the history
* Restore entity attributes so that all attributes are available for offline entities
  • Loading branch information
amosyuen committed Feb 14, 2022
1 parent 6da9806 commit 5249fb4
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 32 deletions.
4 changes: 3 additions & 1 deletion custom_components/tplink_deco/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ async def async_create_coordinator(
entry.entry_id,
)
data = {}

# Populate client list with existing entries so that we keep track of disconnected clients
# since deco list_clients only returns connected clients.
for entry in existing_entries:
if entry.domain == DEVICE_TRACKER_DOMAIN:
client = TPLinkDecoClient(api.host, entry.unique_id)
Expand All @@ -75,7 +78,6 @@ async def async_setup(hass: HomeAssistant, config: Config):

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
"""Set up this integration using UI."""
_LOGGER.debug("async_setup_entry")
if hass.data.get(DOMAIN) is None:
hass.data.setdefault(DOMAIN, {})
_LOGGER.info(STARTUP_MESSAGE)
Expand Down
20 changes: 13 additions & 7 deletions custom_components/tplink_deco/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ def __init__(self, router_ip: str, mac: str) -> None:
self.online = False
self.connection_type = None
self.interface = None
self.down_kilobytes_per_s = None
self.up_kilobytes_per_s = None
self.down_kilobytes_per_s = 0
self.up_kilobytes_per_s = 0
self.last_activity = None

def update(
Expand All @@ -64,20 +64,23 @@ def __init__(
api: TplinkDecoApi,
scan_interval_seconds: int,
consider_home_seconds: int,
data: dict[str:TPLinkDecoClient],
data: dict[str:TPLinkDecoClient] = {},
) -> None:
"""Initialize."""
self._api = api
self._consider_home_seconds = consider_home_seconds
self._on_close: list[Callable] = []
self.data = data

super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=scan_interval_seconds),
)
# Must happen after super().__init__
if len(data) > 0:
self.data = data
async_dispatcher_send(self.hass, SIGNAL_CLIENT_ADDED)

async def _async_update_data(self):
"""Update data via api."""
Expand Down Expand Up @@ -107,9 +110,12 @@ async def _async_update_data(self):
mac = client.mac
if mac not in data:
data[mac] = client
client.online = (
utc_point_in_time - client.last_activity
).total_seconds() < self._consider_home_seconds
if client.last_activity is None:
client.online = False
else:
client.online = (
utc_point_in_time - client.last_activity
).total_seconds() < self._consider_home_seconds

if client_added:
async_dispatcher_send(self.hass, SIGNAL_CLIENT_ADDED)
Expand Down
86 changes: 62 additions & 24 deletions custom_components/tplink_deco/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

from homeassistant.components.device_tracker import SOURCE_TYPE_ROUTER
from homeassistant.components.device_tracker.config_entry import ScannerEntity
from homeassistant.components.device_tracker.const import ATTR_IP
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import callback
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
Expand All @@ -17,6 +19,11 @@

_LOGGER: logging.Logger = logging.getLogger(__package__)

ATTR_CONNECTION_TYPE = "connection_type"
ATTR_DOWN_KILOBYTES_PER_S = "down_kilobytes_per_s"
ATTR_INTERFACE = "interface"
ATTR_UP_KILOBYTES_PER_S = "up_kilobytes_per_s"


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
Expand Down Expand Up @@ -49,31 +56,49 @@ def add_untracked_entities():
add_untracked_entities()


class TplinkDecoDeviceTracker(CoordinatorEntity, ScannerEntity):
class TplinkDecoDeviceTracker(CoordinatorEntity, RestoreEntity, ScannerEntity):
"""TP Link Deco Entity."""

def __init__(
self, coordinator: TplinkDecoDataUpdateCoordinator, client: TPLinkDecoClient
) -> None:
"""Initialize a TP-Link Deco device."""
self._attr_connection_type = None
self._attr_interface = None
self._attr_ip_address = None
self._attr_name = None
self._client = client
self._mac_address = client.mac
self._update_from_client()
super().__init__(coordinator)

async def async_added_to_hass(self):
"""Run when entity about to be added."""
await super().async_added_to_hass()

# Restore old state
old_state = await self.async_get_last_state()
if old_state is not None:
if self._attr_connection_type is None:
self._attr_connection_type = old_state.attributes.get(
ATTR_CONNECTION_TYPE
)
if self._attr_interface is None:
self._attr_interface = old_state.attributes.get(ATTR_INTERFACE)
if self._attr_ip_address is None:
self._attr_ip_address = old_state.attributes.get(ATTR_IP)
self.async_write_ha_state()

@property
def unique_id(self):
"""Return a unique ID to use for this entity."""
return self.mac_address
def mac_address(self):
"""Return the MAC address."""
return self._mac_address

@property
def source_type(self):
"""Return the source type."""
return SOURCE_TYPE_ROUTER

@property
def name(self):
"""Return the name for this entity."""
return self._client.name

@property
def icon(self) -> str:
"""Return device icon."""
Expand All @@ -84,24 +109,14 @@ def is_connected(self):
"""Return true if the device is connected to the router."""
return self._client.online

@property
def ip_address(self) -> str:
"""Return the primary ip address of the device."""
return self._client.ip_address

@property
def mac_address(self) -> str:
"""Return the mac address of the device."""
return self._client.mac

@property
def extra_state_attributes(self):
"""Return extra state attributes."""
return {
"connection_type": self._client.connection_type,
"interface": self._client.interface,
"down_kilobytes_per_s": self._client.down_kilobytes_per_s,
"up_kilobytes_per_s": self._client.up_kilobytes_per_s,
ATTR_CONNECTION_TYPE: self._attr_connection_type,
ATTR_INTERFACE: self._attr_interface,
ATTR_DOWN_KILOBYTES_PER_S: self._client.down_kilobytes_per_s,
ATTR_UP_KILOBYTES_PER_S: self._client.up_kilobytes_per_s,
}

@property
Expand All @@ -115,5 +130,28 @@ def device_info(self) -> DeviceInfo:

@callback
async def async_on_demand_update(self):
"""Update state."""
"""Request update from coordinator."""
await self.coordinator.async_request_refresh()

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if self._update_from_client():
self.async_write_ha_state()

def _update_from_client(self) -> None:
"""Update data from client."""
changed = False
if self._client.connection_type is not None:
self._attr_connection_type = self._client.connection_type
changed = True
if self._client.interface is not None:
self._attr_interface = self._client.interface
changed = True
if self._client.ip_address is not None:
self._attr_ip_address = self._client.ip_address
changed = True
if self._client.name is not None:
self._attr_name = self._client.name
changed = True
return changed

0 comments on commit 5249fb4

Please sign in to comment.