1 """Represent the AsusWrt router."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from datetime
import datetime, timedelta
8 from types
import MappingProxyType
11 from pyasuswrt
import AsusWrtError
15 DEFAULT_CONSIDER_HOME,
16 DOMAIN
as TRACKER_DOMAIN,
28 from .bridge
import AsusWrtBridge, WrtDevice
36 DEFAULT_TRACK_UNKNOWN,
41 SENSORS_CONNECTED_DEVICE,
44 CONF_REQ_RELOAD = [CONF_DNSMASQ, CONF_INTERFACE, CONF_REQUIRE_IP]
48 SENSORS_TYPE_COUNT =
"sensors_count"
50 _LOGGER = logging.getLogger(__name__)
54 """Data handler for AsusWrt sensor."""
56 def __init__(self, hass: HomeAssistant, api: AsusWrtBridge) ->
None:
57 """Initialize a AsusWrt sensor data handler."""
63 """Return number of connected devices."""
67 """Update connected devices attribute."""
76 update_method: Callable[[], Any] |
None =
None,
77 ) -> DataUpdateCoordinator:
78 """Get the coordinator for a specific sensor type."""
80 if sensor_type == SENSORS_TYPE_COUNT:
83 elif update_method
is not None:
84 method = update_method
86 raise RuntimeError(f
"Invalid sensor type: {sensor_type}")
94 update_interval=SCAN_INTERVAL
if should_poll
else None,
96 await coordinator.async_refresh()
102 """Representation of a AsusWrt device info."""
104 def __init__(self, mac: str, name: str |
None =
None) ->
None:
105 """Initialize a AsusWrt device info."""
112 def update(self, dev_info: WrtDevice |
None =
None, consider_home: int = 0) ->
None:
113 """Update AsusWrt device info."""
114 utc_point_in_time = dt_util.utcnow()
116 if not self.
_name_name:
117 self.
_name_name = dev_info.name
or self.
_mac_mac.replace(
":",
"_")
125 and (utc_point_in_time - self.
_last_activity_last_activity).total_seconds()
132 """Return connected status."""
137 """Return device mac address."""
142 """Return device name."""
143 return self.
_name_name
147 """Return device ip address."""
152 """Return device last activity."""
157 """Representation of a AsusWrt router."""
159 def __init__(self, hass: HomeAssistant, entry: ConfigEntry) ->
None:
160 """Initialize a AsusWrt router."""
164 self._devices: dict[str, AsusWrtDevInfo] = {}
169 self._sensors_coordinator: dict[str, Any] = {}
171 self._on_close: list[Callable] = []
173 self._options: dict[str, Any] = {
174 CONF_DNSMASQ: DEFAULT_DNSMASQ,
175 CONF_INTERFACE: DEFAULT_INTERFACE,
176 CONF_REQUIRE_IP:
True,
178 self._options.
update(entry.options)
180 self._api: AsusWrtBridge = AsusWrtBridge.get_bridge(
185 """Migrate router entities to new unique id format."""
186 _ENTITY_MIGRATION_ID = {
187 "sensor_connected_device":
"Devices Connected",
188 "sensor_rx_bytes":
"Download",
189 "sensor_tx_bytes":
"Upload",
190 "sensor_rx_rates":
"Download Speed",
191 "sensor_tx_rates":
"Upload Speed",
192 "sensor_load_avg1":
"Load Avg (1m)",
193 "sensor_load_avg5":
"Load Avg (5m)",
194 "sensor_load_avg15":
"Load Avg (15m)",
195 "2.4GHz":
"2.4GHz Temperature",
196 "5.0GHz":
"5GHz Temperature",
197 "CPU":
"CPU Temperature",
200 entity_reg = er.async_get(self.
hasshass)
201 router_entries = er.async_entries_for_config_entry(
202 entity_reg, self.
_entry_entry.entry_id
205 migrate_entities: dict[str, str] = {}
206 for entry
in router_entries:
207 if entry.domain == TRACKER_DOMAIN:
209 old_unique_id = entry.unique_id
210 if not old_unique_id.startswith(DOMAIN):
212 for new_id, old_id
in _ENTITY_MIGRATION_ID.items():
213 if old_unique_id.endswith(old_id):
214 migrate_entities[entry.entity_id] =
slugify(
215 f
"{self.unique_id}_{new_id}"
219 for entity_id, unique_id
in migrate_entities.items():
220 entity_reg.async_update_entity(entity_id, new_unique_id=unique_id)
223 """Set up a AsusWrt router."""
225 await self._api.async_connect()
226 except (AsusWrtError, OSError)
as exc:
227 raise ConfigEntryNotReady
from exc
228 if not self._api.is_connected:
229 raise ConfigEntryNotReady
232 entity_reg = er.async_get(self.
hasshass)
233 track_entries = er.async_entries_for_config_entry(
234 entity_reg, self.
_entry_entry.entry_id
236 for entry
in track_entries:
237 if entry.domain != TRACKER_DOMAIN:
242 if device_mac != entry.unique_id:
243 existing_entity_id = entity_reg.async_get_entity_id(
244 TRACKER_DOMAIN, DOMAIN, device_mac
246 if existing_entity_id:
249 entity_reg.async_remove(entry.entity_id)
252 entity_reg.async_update_entity(
253 entry.entity_id, new_unique_id=device_mac
256 self._devices[device_mac] =
AsusWrtDevInfo(device_mac, entry.original_name)
271 async
def update_all(self, now: datetime |
None =
None) ->
None:
272 """Update all AsusWrt platforms."""
276 """Update AsusWrt devices tracker."""
278 _LOGGER.debug(
"Checking devices for ASUS router %s", self.
hosthost)
280 wrt_devices = await self._api.async_get_connected_devices()
281 except (OSError, AsusWrtError)
as exc:
285 "Error connecting to ASUS router %s for device update: %s",
293 _LOGGER.warning(
"Reconnected to ASUS router %s", self.
hosthost)
296 consider_home: int = self._options.
get(
297 CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds()
299 track_unknown: bool = self._options.
get(
300 CONF_TRACK_UNKNOWN, DEFAULT_TRACK_UNKNOWN
303 for device_mac, device
in self._devices.items():
304 dev_info = wrt_devices.pop(device_mac,
None)
305 device.update(dev_info, consider_home)
307 for device_mac, dev_info
in wrt_devices.items():
308 if not track_unknown
and not dev_info.name:
312 device.update(dev_info)
313 self._devices[device_mac] = device
321 """Init AsusWrt sensors coordinators."""
328 sensors_types = await self._api.async_get_available_sensors()
329 sensors_types[SENSORS_TYPE_COUNT] = {KEY_SENSORS: SENSORS_CONNECTED_DEVICE}
331 for sensor_type, sensor_def
in sensors_types.items():
332 if not (sensor_names := sensor_def.get(KEY_SENSORS)):
335 sensor_type, update_method=sensor_def.get(KEY_METHOD)
337 self._sensors_coordinator[sensor_type] = {
338 KEY_COORDINATOR: coordinator,
339 KEY_SENSORS: sensor_names,
343 """Request refresh for AsusWrt unpolled sensors."""
347 if SENSORS_TYPE_COUNT
in self._sensors_coordinator:
348 coordinator = self._sensors_coordinator[SENSORS_TYPE_COUNT][KEY_COORDINATOR]
350 await coordinator.async_refresh()
353 """Close the connection."""
354 if self._api
is not None:
355 await self._api.async_disconnect()
357 for func
in self._on_close:
359 self._on_close.clear()
363 """Add a function to call when router is closed."""
364 self._on_close.append(func)
367 """Update router options."""
369 for name, new_opt
in new_options.items():
370 if name
in CONF_REQ_RELOAD:
371 old_opt = self._options.
get(name)
372 if old_opt
is None or old_opt != new_opt:
376 self._options.
update(new_options)
381 """Return the device information."""
383 identifiers={(DOMAIN, self.
_entry_entry.unique_id
or "AsusWRT")},
385 model=self._api.model
or "Asus Router",
387 configuration_url=f
"http://{self.host}",
389 if self._api.firmware:
390 info[
"sw_version"] = self._api.firmware
396 """Event specific per AsusWrt entry to signal new device."""
397 return f
"{DOMAIN}-device-new"
401 """Event specific per AsusWrt entry to signal updates in devices."""
402 return f
"{DOMAIN}-device-update"
406 """Return router hostname."""
407 return self._api.host
411 """Return router unique id."""
412 return self.
_entry_entry.unique_id
or self.
_entry_entry.entry_id
415 def devices(self) -> dict[str, AsusWrtDevInfo]:
416 """Return devices."""
421 """Return sensors coordinators."""
422 return self._sensors_coordinator
str|None ip_address(self)
None update(self, WrtDevice|None dev_info=None, int consider_home=0)
None __init__(self, str mac, str|None name=None)
datetime|None last_activity(self)
None __init__(self, HomeAssistant hass, ConfigEntry entry)
None _migrate_entities_unique_id(self)
None update_all(self, datetime|None now=None)
None init_sensors_coordinator(self)
DeviceInfo device_info(self)
dict[str, AsusWrtDevInfo] devices(self)
dict[str, Any] sensors_coordinator(self)
None update_devices(self)
None async_on_close(self, CALLBACK_TYPE func)
bool update_options(self, MappingProxyType[str, Any] new_options)
str signal_device_new(self)
str signal_device_update(self)
None _update_unpolled_sensors(self)
None __init__(self, HomeAssistant hass, AsusWrtBridge api)
dict[str, int] _get_connected_devices(self)
bool update_device_count(self, int conn_devices)
DataUpdateCoordinator get_coordinator(self, str sensor_type, Callable[[], Any]|None update_method=None)
web.Response get(self, web.Request request, str config_key)
IssData update(pyiss.ISS iss)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)
CALLBACK_TYPE async_track_time_interval(HomeAssistant hass, Callable[[datetime], Coroutine[Any, Any, None]|None] action, timedelta interval, *str|None name=None, bool|None cancel_on_shutdown=None)