Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions custom_components/eero/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME, CONF_SCAN_INTERVAL, Platform
from homeassistant.helpers.typing import UNDEFINED
from homeassistant.core import HomeAssistant
from homeassistant.helpers import (
config_validation as cv,
Expand Down Expand Up @@ -346,7 +347,10 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
device_entry.name,
device_entry.model,
)
device_registry.async_remove_device(device_entry.id)
try:
device_registry.async_remove_device(device_entry.id)
except (KeyError, ValueError):
pass
else:
for entity_entry in er.async_entries_for_device(
entity_registry, device_entry.id
Expand Down Expand Up @@ -395,7 +399,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
conf_update = {}
for network_id, resources in conf_resources.items():
conf_update[network_id] = EeroUpdateConfig(
activity=conf_activity[network_id],
activity=conf_activity.get(network_id, {}),
profiles=resources[CONF_PROFILES],
get_backup_access_points=bool(resources[CONF_BACKUP_NETWORKS]),
get_devices=any(
Expand Down Expand Up @@ -562,6 +566,7 @@ def __init__(
) -> None:
"""Initialize device."""
super().__init__(coordinator)
self._attr_has_entity_name = True
self.network_id = network_id
self.resource_id = resource_id
self.entity_description = description
Expand All @@ -570,15 +575,15 @@ def __init__(

@property
def network(self) -> EeroNetwork | None:
"""Return the state attributes."""
"""Return the network for this entity."""
for network in self.coordinator.data.networks:
if network.id == self.network_id:
return network
return None

@property
def resource(self) -> EeroResource | None:
"""Return the state attributes."""
"""Return the resource for this entity."""
if self.resource_id:
for resource in self.network.resources:
if resource.id == self.resource_id:
Expand All @@ -592,13 +597,24 @@ def unique_id(self) -> str:
return f"{self.network.id}-{self.entity_description.key}"
return f"{self.network.id}-{self.resource.id}-{self.entity_description.key}"

def _device_name(self) -> str:
"""Return the Home Assistant device name for this resource."""
if self.resource.is_client and self.suffix_connection_type:
name = self.resource.name_connection_type
else:
name = self.resource.name

if not self.resource.is_network and self.prefix_network_name:
return f"{self.network.name} {name}"
return name

@property
def device_info(self) -> dr.DeviceInfo:
"""Return device specific attributes.

Implemented by platform classes.
"""
name = self.resource.name
name = self._device_name()
if self.resource.is_network:
model = MODEL_NETWORK
elif self.resource.is_backup_network:
Expand All @@ -611,8 +627,6 @@ def device_info(self) -> dr.DeviceInfo:
model = (
MODEL_CLIENT_WIRELESS if self.resource.wireless else MODEL_CLIENT_WIRED
)
if self.suffix_connection_type:
name = self.resource.name_connection_type

entry_type, suggested_area, sw_version, hw_version, via_device = (
None,
Expand Down Expand Up @@ -656,25 +670,11 @@ def device_info(self) -> dr.DeviceInfo:
)

@property
def name(self) -> str:
def name(self) -> str | None:
"""Return the name of the entity."""
if self.resource.is_client:
name = self.resource.name
if self.suffix_connection_type:
name = self.resource.name_connection_type
if self.prefix_network_name:
name = f"{self.network.name} {name}"
return f"{name} {self.entity_description.name}"
if (
self.resource.is_backup_network
or self.resource.is_eero
or self.resource.is_profile
):
name = f"{self.resource.name} {self.entity_description.name}"
if self.prefix_network_name:
name = f"{self.network.name} {name}"
return name
return f"{self.resource.name} {self.entity_description.name}"
if self.entity_description.name in (None, UNDEFINED):
return None
return self.entity_description.name


@dataclass
Expand Down
1 change: 0 additions & 1 deletion custom_components/eero/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ def save_response(self, response: dict[str, Any] | None, name="response") -> Non
default=lambda o: "not-serializable",
sort_keys=True,
)
file.close()

def update(
self,
Expand Down
14 changes: 6 additions & 8 deletions custom_components/eero/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,17 @@ def channel(self) -> int | None:
def channel_width_rx(self) -> str | None:
"""Channel width RX."""
return (
self.data.get("connectivity", {})
.get("rx_rate_info", {})
.get("channel_width")
)
(self.data.get("connectivity") or {})
.get("rx_rate_info") or {}
).get("channel_width")

@property
def channel_width_tx(self) -> str | None:
"""Channel width TX."""
return (
self.data.get("connectivity", {})
.get("tx_rate_info", {})
.get("channel_width")
)
(self.data.get("connectivity") or {})
.get("tx_rate_info") or {}
).get("channel_width")

@property
def connected(self) -> bool | None:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/eero/api/eero.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def set_status_light_brightness(self, value: int) -> None:
if not isinstance(value, int):
return None
if not value:
return self.set_status_light_off
return self.set_status_light_off()
self.api.call(
method=METHOD_PUT,
url=self.url_led,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/eero/api/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def ad_block_status(self) -> str:

@property
def adblock_day(self) -> int | None:
"""Adblock dasy."""
"""Adblock day."""
for series in (
self.data.get("activity", {}).get("network", {}).get("adblock_day", [])
):
Expand Down
43 changes: 4 additions & 39 deletions custom_components/eero/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@
from collections.abc import Mapping
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Any, final
from typing import Any

from homeassistant.components.device_tracker import (
ATTR_HOST_NAME,
ATTR_IP,
ATTR_MAC,
ATTR_SOURCE_TYPE,
BaseScannerEntity,
SourceType,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_MANUFACTURER, STATE_HOME, STATE_NOT_HOME
from homeassistant.const import ATTR_MANUFACTURER
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util import dt as dt_util

Expand Down Expand Up @@ -100,7 +96,7 @@ async def async_setup_entry(
async_add_entities(entities)


class EeroDeviceTrackerEntity(EeroEntity):
class EeroDeviceTrackerEntity(EeroEntity, BaseScannerEntity):
"""Representation of an Eero device tracker entity."""

_attr_entity_category = EntityCategory.DIAGNOSTIC
Expand All @@ -126,17 +122,6 @@ def __init__(
)
self.last_seen: datetime | None = None

@property
def name(self) -> str | None:
"""Return the name of the entity."""
if self.resource.is_client and self.suffix_connection_type:
name = self.resource.name_connection_type
else:
name = self.resource.name
if self.prefix_network_name:
return f"{self.network.name} {name}"
return name

@property
def is_connected(self) -> bool | None:
"""Return true if the device is connected to the network."""
Expand Down Expand Up @@ -176,26 +161,6 @@ def hostname(self) -> str | None:
return self.resource.hostname
return None

@property
def state(self) -> str:
"""Return the state of the device."""
if self.is_connected:
return STATE_HOME
return STATE_NOT_HOME

@final
@property
def state_attributes(self) -> dict[str, StateType]:
"""Return the device state attributes."""
attr: dict[str, StateType] = {ATTR_SOURCE_TYPE: self.source_type}
if ip_address := self.ip_address:
attr[ATTR_IP] = ip_address
if mac_address := self.mac_address:
attr[ATTR_MAC] = mac_address
if hostname := self.hostname:
attr[ATTR_HOST_NAME] = hostname
return attr

@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return entity specific state attributes.
Expand Down
6 changes: 3 additions & 3 deletions custom_components/eero/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class EeroSensorEntityDescription(EeroEntityDescription, SensorEntityDescription
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.TOTAL_INCREASING,
native_value=lambda resource, key: (
getattr(resource, key)[0] + getattr(resource, key)[1]
(getattr(resource, key)[0] or 0) + (getattr(resource, key)[1] or 0)
),
native_unit_of_measurement=UnitOfInformation.BYTES,
activity_type=True,
Expand All @@ -168,7 +168,7 @@ class EeroSensorEntityDescription(EeroEntityDescription, SensorEntityDescription
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.TOTAL_INCREASING,
native_value=lambda resource, key: (
getattr(resource, key)[0] + getattr(resource, key)[1]
(getattr(resource, key)[0] or 0) + (getattr(resource, key)[1] or 0)
),
native_unit_of_measurement=UnitOfInformation.BYTES,
activity_type=True,
Expand All @@ -179,7 +179,7 @@ class EeroSensorEntityDescription(EeroEntityDescription, SensorEntityDescription
device_class=SensorDeviceClass.DATA_SIZE,
state_class=SensorStateClass.TOTAL_INCREASING,
native_value=lambda resource, key: (
getattr(resource, key)[0] + getattr(resource, key)[1]
(getattr(resource, key)[0] or 0) + (getattr(resource, key)[1] or 0)
),
native_unit_of_measurement=UnitOfInformation.BYTES,
activity_type=True,
Expand Down
6 changes: 5 additions & 1 deletion custom_components/eero/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ def title(self) -> str | None:
This helps to differentiate between the device or entity name
versus the title of the software installed.
"""
return self.resource.target_firmware.title
# The API returns e.g. "eeroOS 7.16.1" but HA appends latest_version
# alongside the title, causing duplication. Strip the version suffix.
if title := self.resource.target_firmware.title:
return title.rsplit(" ", 1)[0] if " " in title else title
return None

def install(self, version: str | None, backup: bool, **kwargs: Any) -> None:
"""Install an update.
Expand Down