diff --git a/homeassistant/components/freebox/__init__.py b/homeassistant/components/freebox/__init__.py index ee8c2f30975..b5deb8517bb 100644 --- a/homeassistant/components/freebox/__init__.py +++ b/homeassistant/components/freebox/__init__.py @@ -1,18 +1,19 @@ """Support for Freebox devices (Freebox v6 and Freebox mini 4K).""" -import logging +from datetime import timedelta +from freebox_api.exceptions import HttpRequestError import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP -from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.core import Event, HomeAssistant, ServiceCall +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.typing import ConfigType from .const import DOMAIN, PLATFORMS, SERVICE_REBOOT -from .router import FreeboxRouter - -_LOGGER = logging.getLogger(__name__) +from .router import FreeboxRouter, get_api FREEBOX_SCHEMA = vol.Schema( {vol.Required(CONF_HOST): cv.string, vol.Required(CONF_PORT): cv.port} @@ -26,6 +27,8 @@ CONFIG_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) +SCAN_INTERVAL = timedelta(seconds=30) + async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up the Freebox integration.""" @@ -42,8 +45,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Freebox entry.""" - router = FreeboxRouter(hass, entry) - await router.setup() + api = await get_api(hass, entry.data[CONF_HOST]) + try: + await api.open(entry.data[CONF_HOST], entry.data[CONF_PORT]) + except HttpRequestError as err: + raise ConfigEntryNotReady from err + + freebox_config = await api.system.get_config() + + router = FreeboxRouter(hass, entry, api, freebox_config) + await router.update_all() + entry.async_on_unload( + async_track_time_interval(hass, router.update_all, SCAN_INTERVAL) + ) hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.unique_id] = router @@ -57,7 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.services.async_register(DOMAIN, SERVICE_REBOOT, async_reboot) - async def async_close_connection(event): + async def async_close_connection(event: Event) -> None: """Close Freebox connection on HA Stop.""" await router.close() @@ -72,7 +86,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: - router = hass.data[DOMAIN].pop(entry.unique_id) + router: FreeboxRouter = hass.data[DOMAIN].pop(entry.unique_id) await router.close() hass.services.async_remove(DOMAIN, SERVICE_REBOOT) diff --git a/homeassistant/components/freebox/device_tracker.py b/homeassistant/components/freebox/device_tracker.py index d57af2d53c2..0fe04fe7eb9 100644 --- a/homeassistant/components/freebox/device_tracker.py +++ b/homeassistant/components/freebox/device_tracker.py @@ -19,15 +19,15 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up device tracker for Freebox component.""" - router = hass.data[DOMAIN][entry.unique_id] - tracked = set() + router: FreeboxRouter = hass.data[DOMAIN][entry.unique_id] + tracked: set[str] = set() @callback - def update_router(): + def update_router() -> None: """Update the values of the router.""" add_entities(router, async_add_entities, tracked) - router.listeners.append( + entry.async_on_unload( async_dispatcher_connect(hass, router.signal_device_new, update_router) ) @@ -35,7 +35,9 @@ async def async_setup_entry( @callback -def add_entities(router, async_add_entities, tracked): +def add_entities( + router: FreeboxRouter, async_add_entities: AddEntitiesCallback, tracked: set[str] +) -> None: """Add new tracker entities from the router.""" new_tracked = [] @@ -61,7 +63,7 @@ class FreeboxDevice(ScannerEntity): self._manufacturer = device["vendor_name"] self._icon = icon_for_freebox_device(device) self._active = False - self._attrs = {} + self._attrs: dict[str, Any] = {} @callback def async_update_state(self) -> None: diff --git a/homeassistant/components/freebox/router.py b/homeassistant/components/freebox/router.py index e352146915e..0d20545fdcd 100644 --- a/homeassistant/components/freebox/router.py +++ b/homeassistant/components/freebox/router.py @@ -1,24 +1,23 @@ """Represent the Freebox router and its devices and sensors.""" from __future__ import annotations -from datetime import datetime, timedelta -import logging +from collections.abc import Mapping +from contextlib import suppress +from datetime import datetime import os from pathlib import Path from typing import Any from freebox_api import Freepybox from freebox_api.api.wifi import Wifi -from freebox_api.exceptions import HttpRequestError +from freebox_api.exceptions import NotOpenError from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_PORT from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.event import async_track_time_interval from homeassistant.util import slugify from .const import ( @@ -30,10 +29,6 @@ from .const import ( STORAGE_VERSION, ) -_LOGGER = logging.getLogger(__name__) - -SCAN_INTERVAL = timedelta(seconds=30) - async def get_api(hass: HomeAssistant, host: str) -> Freepybox: """Get the Freebox API.""" @@ -50,18 +45,23 @@ async def get_api(hass: HomeAssistant, host: str) -> Freepybox: class FreeboxRouter: """Representation of a Freebox router.""" - def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: + def __init__( + self, + hass: HomeAssistant, + entry: ConfigEntry, + api: Freepybox, + freebox_config: Mapping[str, Any], + ) -> None: """Initialize a Freebox router.""" self.hass = hass - self._entry = entry self._host = entry.data[CONF_HOST] self._port = entry.data[CONF_PORT] - self._api: Freepybox = None - self.name = None - self.mac = None - self._sw_v = None - self._attrs = {} + self._api: Freepybox = api + self.name: str = freebox_config["model_info"]["pretty_name"] + self.mac: str = freebox_config["mac"] + self._sw_v: str = freebox_config["firmware_version"] + self._attrs: dict[str, Any] = {} self.devices: dict[str, dict[str, Any]] = {} self.disks: dict[int, dict[str, Any]] = {} @@ -69,31 +69,6 @@ class FreeboxRouter: self.sensors_connection: dict[str, float] = {} self.call_list: list[dict[str, Any]] = [] - self._unsub_dispatcher = None - self.listeners = [] - - async def setup(self) -> None: - """Set up a Freebox router.""" - self._api = await get_api(self.hass, self._host) - - try: - await self._api.open(self._host, self._port) - except HttpRequestError: - _LOGGER.exception("Failed to connect to Freebox") - return ConfigEntryNotReady - - # System - fbx_config = await self._api.system.get_config() - self.mac = fbx_config["mac"] - self.name = fbx_config["model_info"]["pretty_name"] - self._sw_v = fbx_config["firmware_version"] - - # Devices & sensors - await self.update_all() - self._unsub_dispatcher = async_track_time_interval( - self.hass, self.update_all, SCAN_INTERVAL - ) - async def update_all(self, now: datetime | None = None) -> None: """Update all Freebox platforms.""" await self.update_device_trackers() @@ -102,7 +77,7 @@ class FreeboxRouter: async def update_device_trackers(self) -> None: """Update Freebox devices.""" new_device = False - fbx_devices: [dict[str, Any]] = await self._api.lan.get_hosts_list() + fbx_devices: list[dict[str, Any]] = await self._api.lan.get_hosts_list() # Adds the Freebox itself fbx_devices.append( @@ -164,7 +139,7 @@ class FreeboxRouter: async def _update_disks_sensors(self) -> None: """Update Freebox disks.""" # None at first request - fbx_disks: [dict[str, Any]] = await self._api.storage.get_disks() or [] + fbx_disks: list[dict[str, Any]] = await self._api.storage.get_disks() or [] for fbx_disk in fbx_disks: self.disks[fbx_disk["id"]] = fbx_disk @@ -175,10 +150,8 @@ class FreeboxRouter: async def close(self) -> None: """Close the connection.""" - if self._api is not None: + with suppress(NotOpenError): await self._api.close() - self._unsub_dispatcher() - self._api = None @property def device_info(self) -> DeviceInfo: diff --git a/homeassistant/components/freebox/sensor.py b/homeassistant/components/freebox/sensor.py index c5852aa22aa..46aa9ee8aa0 100644 --- a/homeassistant/components/freebox/sensor.py +++ b/homeassistant/components/freebox/sensor.py @@ -27,7 +27,7 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the sensors.""" - router = hass.data[DOMAIN][entry.unique_id] + router: FreeboxRouter = hass.data[DOMAIN][entry.unique_id] entities = [] _LOGGER.debug( @@ -120,7 +120,7 @@ class FreeboxCallSensor(FreeboxSensor): ) -> None: """Initialize a Freebox call sensor.""" super().__init__(router, description) - self._call_list_for_type = [] + self._call_list_for_type: list[dict[str, Any]] = [] @callback def async_update_state(self) -> None: diff --git a/homeassistant/components/freebox/switch.py b/homeassistant/components/freebox/switch.py index 76b89a17414..ee59c097f93 100644 --- a/homeassistant/components/freebox/switch.py +++ b/homeassistant/components/freebox/switch.py @@ -21,7 +21,7 @@ async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up the switch.""" - router = hass.data[DOMAIN][entry.unique_id] + router: FreeboxRouter = hass.data[DOMAIN][entry.unique_id] async_add_entities([FreeboxWifiSwitch(router)], True) @@ -31,7 +31,7 @@ class FreeboxWifiSwitch(SwitchEntity): def __init__(self, router: FreeboxRouter) -> None: """Initialize the Wifi switch.""" self._name = "Freebox WiFi" - self._state = None + self._state: bool | None = None self._router = router self._unique_id = f"{self._router.mac} {self._name}" @@ -46,7 +46,7 @@ class FreeboxWifiSwitch(SwitchEntity): return self._name @property - def is_on(self) -> bool: + def is_on(self) -> bool | None: """Return true if device is on.""" return self._state diff --git a/mypy.ini b/mypy.ini index cd03f8dc8b5..e947b661618 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2050,9 +2050,6 @@ ignore_errors = true [mypy-homeassistant.components.firmata.*] ignore_errors = true -[mypy-homeassistant.components.freebox.*] -ignore_errors = true - [mypy-homeassistant.components.geniushub.*] ignore_errors = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index 00c22abae4a..2ab8739de89 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -26,7 +26,6 @@ IGNORED_MODULES: Final[list[str]] = [ "homeassistant.components.evohome.*", "homeassistant.components.fireservicerota.*", "homeassistant.components.firmata.*", - "homeassistant.components.freebox.*", "homeassistant.components.geniushub.*", "homeassistant.components.google_assistant.*", "homeassistant.components.gree.*",