From c76d2c4283234f6b434bea8348d1cc8cd17ab4b3 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Wed, 23 Feb 2022 01:35:48 +0100 Subject: [PATCH] Fritz device_trackers for non mesh devices (#67006) --- homeassistant/components/fritz/common.py | 76 +++++++++++++------ homeassistant/components/fritz/config_flow.py | 9 +++ homeassistant/components/fritz/const.py | 3 + homeassistant/components/fritz/strings.json | 3 +- .../components/fritz/translations/en.json | 14 +--- 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index 5706ca19486..b2a429bfa3c 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -39,6 +39,8 @@ from homeassistant.helpers.entity import DeviceInfo from homeassistant.util import dt as dt_util from .const import ( + CONF_OLD_DISCOVERY, + DEFAULT_CONF_OLD_DISCOVERY, DEFAULT_DEVICE_NAME, DEFAULT_HOST, DEFAULT_PORT, @@ -325,27 +327,33 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): """Wrap up FritzboxTools class scan.""" await self.hass.async_add_executor_job(self.scan_devices, now) + def manage_device_info( + self, dev_info: Device, dev_mac: str, consider_home: bool + ) -> bool: + """Update device lists.""" + _LOGGER.debug("Client dev_info: %s", dev_info) + + if dev_mac in self._devices: + self._devices[dev_mac].update(dev_info, consider_home) + return False + + device = FritzDevice(dev_mac, dev_info.name) + device.update(dev_info, consider_home) + self._devices[dev_mac] = device + return True + + def send_signal_device_update(self, new_device: bool) -> None: + """Signal device data updated.""" + dispatcher_send(self.hass, self.signal_device_update) + if new_device: + dispatcher_send(self.hass, self.signal_device_new) + def scan_devices(self, now: datetime | None = None) -> None: """Scan for new devices and return a list of found device ids.""" _LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host) self._update_available, self._latest_firmware = self._update_device_info() - topology: dict = {} - if ( - "Hosts1" not in self.connection.services - or "X_AVM-DE_GetMeshListPath" - not in self.connection.services["Hosts1"].actions - ): - self.mesh_role = MeshRoles.NONE - else: - try: - topology = self.fritz_hosts.get_mesh_topology() - except FritzActionError: - self.mesh_role = MeshRoles.SLAVE - # Avoid duplicating device trackers - return - _LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host) _default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds() if self._options: @@ -371,6 +379,32 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): wan_access=None, ) + if ( + "Hosts1" not in self.connection.services + or "X_AVM-DE_GetMeshListPath" + not in self.connection.services["Hosts1"].actions + ) or ( + self._options + and self._options.get(CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY) + ): + _LOGGER.debug( + "Using old hosts discovery method. (Mesh not supported or user option)" + ) + self.mesh_role = MeshRoles.NONE + for mac, info in hosts.items(): + if self.manage_device_info(info, mac, consider_home): + new_device = True + self.send_signal_device_update(new_device) + return + + try: + if not (topology := self.fritz_hosts.get_mesh_topology()): + raise Exception("Mesh supported but empty topology reported") + except FritzActionError: + self.mesh_role = MeshRoles.SLAVE + # Avoid duplicating device trackers + return + mesh_intf = {} # first get all meshed devices for node in topology.get("nodes", []): @@ -414,19 +448,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): dev_info.connected_to = intf["device"] dev_info.connection_type = intf["type"] dev_info.ssid = intf.get("ssid") - _LOGGER.debug("Client dev_info: %s", dev_info) - if dev_mac in self._devices: - self._devices[dev_mac].update(dev_info, consider_home) - else: - device = FritzDevice(dev_mac, dev_info.name) - device.update(dev_info, consider_home) - self._devices[dev_mac] = device + if self.manage_device_info(dev_info, dev_mac, consider_home): new_device = True - dispatcher_send(self.hass, self.signal_device_update) - if new_device: - dispatcher_send(self.hass, self.signal_device_new) + self.send_signal_device_update(new_device) async def async_trigger_firmware_update(self) -> bool: """Trigger firmware update.""" diff --git a/homeassistant/components/fritz/config_flow.py b/homeassistant/components/fritz/config_flow.py index 180a6239d9f..0844d725522 100644 --- a/homeassistant/components/fritz/config_flow.py +++ b/homeassistant/components/fritz/config_flow.py @@ -21,6 +21,8 @@ from homeassistant.data_entry_flow import FlowResult from .common import AvmWrapper from .const import ( + CONF_OLD_DISCOVERY, + DEFAULT_CONF_OLD_DISCOVERY, DEFAULT_HOST, DEFAULT_PORT, DOMAIN, @@ -107,6 +109,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN): }, options={ CONF_CONSIDER_HOME: DEFAULT_CONSIDER_HOME.total_seconds(), + CONF_OLD_DISCOVERY: DEFAULT_CONF_OLD_DISCOVERY, }, ) @@ -296,6 +299,12 @@ class FritzBoxToolsOptionsFlowHandler(OptionsFlow): CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds() ), ): vol.All(vol.Coerce(int), vol.Clamp(min=0, max=900)), + vol.Optional( + CONF_OLD_DISCOVERY, + default=self.config_entry.options.get( + CONF_OLD_DISCOVERY, DEFAULT_CONF_OLD_DISCOVERY + ), + ): bool, } ) return self.async_show_form(step_id="init", data_schema=data_schema) diff --git a/homeassistant/components/fritz/const.py b/homeassistant/components/fritz/const.py index 635460db15f..f33cf463996 100644 --- a/homeassistant/components/fritz/const.py +++ b/homeassistant/components/fritz/const.py @@ -32,6 +32,9 @@ PLATFORMS = [ Platform.SWITCH, ] +CONF_OLD_DISCOVERY = "old_discovery" +DEFAULT_CONF_OLD_DISCOVERY = False + DATA_FRITZ = "fritz_data" DSL_CONNECTION: Literal["dsl"] = "dsl" diff --git a/homeassistant/components/fritz/strings.json b/homeassistant/components/fritz/strings.json index f1cdb719741..450566f101b 100644 --- a/homeassistant/components/fritz/strings.json +++ b/homeassistant/components/fritz/strings.json @@ -45,7 +45,8 @@ "step": { "init": { "data": { - "consider_home": "Seconds to consider a device at 'home'" + "consider_home": "Seconds to consider a device at 'home'", + "old_discovery": "Enable old discovery method" } } } diff --git a/homeassistant/components/fritz/translations/en.json b/homeassistant/components/fritz/translations/en.json index 0fa47bd8328..0a58ee686f3 100644 --- a/homeassistant/components/fritz/translations/en.json +++ b/homeassistant/components/fritz/translations/en.json @@ -9,7 +9,6 @@ "already_configured": "Device is already configured", "already_in_progress": "Configuration flow is already in progress", "cannot_connect": "Failed to connect", - "connection_error": "Failed to connect", "invalid_auth": "Invalid authentication" }, "flow_title": "{name}", @@ -30,16 +29,6 @@ "description": "Update FRITZ!Box Tools credentials for: {host}.\n\nFRITZ!Box Tools is unable to log in to your FRITZ!Box.", "title": "Updating FRITZ!Box Tools - credentials" }, - "start_config": { - "data": { - "host": "Host", - "password": "Password", - "port": "Port", - "username": "Username" - }, - "description": "Setup FRITZ!Box Tools to control your FRITZ!Box.\nMinimum needed: username, password.", - "title": "Setup FRITZ!Box Tools - mandatory" - }, "user": { "data": { "host": "Host", @@ -56,7 +45,8 @@ "step": { "init": { "data": { - "consider_home": "Seconds to consider a device at 'home'" + "consider_home": "Seconds to consider a device at 'home'", + "old_discovery": "Enable old discovery method" } } }