Add strict typing to fritzbox (#50486)

* enable strict typing

* apply suggestions

* set defaults for FritzboxConfigFlow

* improvements and suggestions

* another suggestion

* tweaks

* tweaks
This commit is contained in:
Michael 2021-05-15 07:54:11 +02:00 committed by GitHub
parent d37a3cded0
commit 25b2fd0cee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 195 additions and 107 deletions

View file

@ -13,6 +13,7 @@ homeassistant.components.camera.*
homeassistant.components.cover.*
homeassistant.components.device_automation.*
homeassistant.components.elgato.*
homeassistant.components.fritzbox.*
homeassistant.components.frontend.*
homeassistant.components.geo_location.*
homeassistant.components.gios.*

View file

@ -17,14 +17,16 @@ from homeassistant.const import (
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import CONF_CONNECTIONS, CONF_COORDINATOR, DOMAIN, LOGGER, PLATFORMS
from .model import EntityInfo
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@ -63,7 +65,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
data[device.ain] = device
return data
async def async_update_coordinator():
async def async_update_coordinator() -> dict[str, FritzhomeDevice]:
"""Fetch all device data."""
return await hass.async_add_executor_job(_update_fritz_devices)
@ -81,7 +83,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
def logout_fritzbox(event):
def logout_fritzbox(event: Event) -> None:
"""Close connections to this fritzbox."""
fritz.logout()
@ -109,10 +111,10 @@ class FritzBoxEntity(CoordinatorEntity):
def __init__(
self,
entity_info: dict[str, str],
coordinator: DataUpdateCoordinator,
entity_info: EntityInfo,
coordinator: DataUpdateCoordinator[dict[str, FritzhomeDevice]],
ain: str,
):
) -> None:
"""Initialize the FritzBox entity."""
super().__init__(coordinator)
@ -128,7 +130,7 @@ class FritzBoxEntity(CoordinatorEntity):
return self.coordinator.data[self.ain]
@property
def device_info(self):
def device_info(self) -> DeviceInfo:
"""Return device specific attributes."""
return {
"name": self.device.name,
@ -139,21 +141,21 @@ class FritzBoxEntity(CoordinatorEntity):
}
@property
def unique_id(self):
def unique_id(self) -> str:
"""Return the unique ID of the device."""
return self._unique_id
@property
def name(self):
def name(self) -> str:
"""Return the name of the device."""
return self._name
@property
def unit_of_measurement(self):
def unit_of_measurement(self) -> str | None:
"""Return the unit of measurement."""
return self._unit_of_measurement
@property
def device_class(self):
def device_class(self) -> str | None:
"""Return the device class."""
return self._device_class

View file

@ -1,4 +1,6 @@
"""Support for Fritzbox binary sensors."""
from __future__ import annotations
from homeassistant.components.binary_sensor import (
DEVICE_CLASS_WINDOW,
BinarySensorEntity,
@ -21,7 +23,7 @@ async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the FRITZ!SmartHome binary sensor from ConfigEntry."""
entities = []
entities: list[FritzboxBinarySensor] = []
coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR]
for ain, device in coordinator.data.items():
@ -48,8 +50,8 @@ class FritzboxBinarySensor(FritzBoxEntity, BinarySensorEntity):
"""Representation of a binary FRITZ!SmartHome device."""
@property
def is_on(self):
def is_on(self) -> bool:
"""Return true if sensor is on."""
if not self.device.present:
return False
return self.device.alert_state
return self.device.alert_state # type: ignore [no-any-return]

View file

@ -1,4 +1,8 @@
"""Support for AVM FRITZ!SmartHome thermostate devices."""
from __future__ import annotations
from typing import Any
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
ATTR_HVAC_MODE,
@ -34,6 +38,7 @@ from .const import (
CONF_COORDINATOR,
DOMAIN as FRITZBOX_DOMAIN,
)
from .model import ClimateExtraAttributes
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
@ -55,7 +60,7 @@ async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the FRITZ!SmartHome thermostat from ConfigEntry."""
entities = []
entities: list[FritzboxThermostat] = []
coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR]
for ain, device in coordinator.data.items():
@ -82,53 +87,53 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity):
"""The thermostat class for FRITZ!SmartHome thermostates."""
@property
def supported_features(self):
def supported_features(self) -> int:
"""Return the list of supported features."""
return SUPPORT_FLAGS
@property
def available(self):
def available(self) -> bool:
"""Return if thermostat is available."""
return self.device.present
return self.device.present # type: ignore [no-any-return]
@property
def temperature_unit(self):
def temperature_unit(self) -> str:
"""Return the unit of measurement that is used."""
return TEMP_CELSIUS
@property
def precision(self):
def precision(self) -> float:
"""Return precision 0.5."""
return PRECISION_HALVES
@property
def current_temperature(self):
def current_temperature(self) -> float:
"""Return the current temperature."""
return self.device.actual_temperature
return self.device.actual_temperature # type: ignore [no-any-return]
@property
def target_temperature(self):
def target_temperature(self) -> float:
"""Return the temperature we try to reach."""
if self.device.target_temperature == ON_API_TEMPERATURE:
return ON_REPORT_SET_TEMPERATURE
if self.device.target_temperature == OFF_API_TEMPERATURE:
return OFF_REPORT_SET_TEMPERATURE
return self.device.target_temperature
return self.device.target_temperature # type: ignore [no-any-return]
async def async_set_temperature(self, **kwargs):
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if ATTR_HVAC_MODE in kwargs:
hvac_mode = kwargs.get(ATTR_HVAC_MODE)
if kwargs.get(ATTR_HVAC_MODE) is not None:
hvac_mode = kwargs[ATTR_HVAC_MODE]
await self.async_set_hvac_mode(hvac_mode)
elif ATTR_TEMPERATURE in kwargs:
temperature = kwargs.get(ATTR_TEMPERATURE)
elif kwargs.get(ATTR_TEMPERATURE) is not None:
temperature = kwargs[ATTR_TEMPERATURE]
await self.hass.async_add_executor_job(
self.device.set_target_temperature, temperature
)
await self.coordinator.async_refresh()
@property
def hvac_mode(self):
def hvac_mode(self) -> str:
"""Return the current operation mode."""
if (
self.device.target_temperature == OFF_REPORT_SET_TEMPERATURE
@ -139,11 +144,11 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity):
return HVAC_MODE_HEAT
@property
def hvac_modes(self):
def hvac_modes(self) -> list[str]:
"""Return the list of available operation modes."""
return OPERATION_LIST
async def async_set_hvac_mode(self, hvac_mode):
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new operation mode."""
if hvac_mode == HVAC_MODE_OFF:
await self.async_set_temperature(temperature=OFF_REPORT_SET_TEMPERATURE)
@ -153,7 +158,7 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity):
)
@property
def preset_mode(self):
def preset_mode(self) -> str | None:
"""Return current preset mode."""
if self.device.target_temperature == self.device.comfort_temperature:
return PRESET_COMFORT
@ -162,11 +167,11 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity):
return None
@property
def preset_modes(self):
def preset_modes(self) -> list[str]:
"""Return supported preset modes."""
return [PRESET_ECO, PRESET_COMFORT]
async def async_set_preset_mode(self, preset_mode):
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set preset mode."""
if preset_mode == PRESET_COMFORT:
await self.async_set_temperature(
@ -176,19 +181,19 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity):
await self.async_set_temperature(temperature=self.device.eco_temperature)
@property
def min_temp(self):
def min_temp(self) -> int:
"""Return the minimum temperature."""
return MIN_TEMPERATURE
@property
def max_temp(self):
def max_temp(self) -> int:
"""Return the maximum temperature."""
return MAX_TEMPERATURE
@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> ClimateExtraAttributes:
"""Return the device specific state attributes."""
attrs = {
attrs: ClimateExtraAttributes = {
ATTR_STATE_BATTERY_LOW: self.device.battery_low,
ATTR_STATE_DEVICE_LOCKED: self.device.device_lock,
ATTR_STATE_LOCKED: self.device.lock,

View file

@ -1,17 +1,22 @@
"""Config flow for AVM FRITZ!SmartHome."""
from __future__ import annotations
from typing import Any
from urllib.parse import urlparse
from pyfritzhome import Fritzhome, LoginError
from requests.exceptions import HTTPError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.ssdp import (
ATTR_SSDP_LOCATION,
ATTR_UPNP_FRIENDLY_NAME,
ATTR_UPNP_UDN,
)
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.typing import DiscoveryInfoType
from .const import DEFAULT_HOST, DEFAULT_USERNAME, DOMAIN
@ -36,22 +41,22 @@ RESULT_NOT_SUPPORTED = "not_supported"
RESULT_SUCCESS = "success"
class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
class FritzboxConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a AVM FRITZ!SmartHome config flow."""
VERSION = 1
def __init__(self):
def __init__(self) -> None:
"""Initialize flow."""
self._entry = None
self._host = None
self._name = None
self._password = None
self._username = None
self._entry: ConfigEntry | None = None
self._host: str | None = None
self._name: str | None = None
self._password: str | None = None
self._username: str | None = None
def _get_entry(self):
def _get_entry(self, name: str) -> FlowResult:
return self.async_create_entry(
title=self._name,
title=name,
data={
CONF_HOST: self._host,
CONF_PASSWORD: self._password,
@ -59,7 +64,8 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
},
)
async def _update_entry(self):
async def _update_entry(self) -> None:
assert self._entry is not None
self.hass.config_entries.async_update_entry(
self._entry,
data={
@ -70,7 +76,7 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
)
await self.hass.config_entries.async_reload(self._entry.entry_id)
def _try_connect(self):
def _try_connect(self) -> str:
"""Try to connect and check auth."""
fritzbox = Fritzhome(
host=self._host, user=self._username, password=self._password
@ -87,7 +93,9 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
except OSError:
return RESULT_NO_DEVICES_FOUND
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
errors = {}
@ -95,14 +103,14 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
self._host = user_input[CONF_HOST]
self._name = user_input[CONF_HOST]
self._name = str(user_input[CONF_HOST])
self._password = user_input[CONF_PASSWORD]
self._username = user_input[CONF_USERNAME]
result = await self.hass.async_add_executor_job(self._try_connect)
if result == RESULT_SUCCESS:
return self._get_entry()
return self._get_entry(self._name)
if result != RESULT_INVALID_AUTH:
return self.async_abort(reason=result)
errors["base"] = result
@ -111,9 +119,10 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
step_id="user", data_schema=DATA_SCHEMA_USER, errors=errors
)
async def async_step_ssdp(self, discovery_info):
async def async_step_ssdp(self, discovery_info: DiscoveryInfoType) -> FlowResult:
"""Handle a flow initialized by discovery."""
host = urlparse(discovery_info[ATTR_SSDP_LOCATION]).hostname
assert isinstance(host, str)
self.context[CONF_HOST] = host
uuid = discovery_info.get(ATTR_UPNP_UDN)
@ -135,12 +144,14 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="already_configured")
self._host = host
self._name = discovery_info.get(ATTR_UPNP_FRIENDLY_NAME) or host
self._name = str(discovery_info.get(ATTR_UPNP_FRIENDLY_NAME) or host)
self.context["title_placeholders"] = {"name": self._name}
return await self.async_step_confirm()
async def async_step_confirm(self, user_input=None):
async def async_step_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle user-confirmation of discovered node."""
errors = {}
@ -150,7 +161,8 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
result = await self.hass.async_add_executor_job(self._try_connect)
if result == RESULT_SUCCESS:
return self._get_entry()
assert self._name is not None
return self._get_entry(self._name)
if result != RESULT_INVALID_AUTH:
return self.async_abort(reason=result)
errors["base"] = result
@ -162,16 +174,20 @@ class FritzboxConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors=errors,
)
async def async_step_reauth(self, data):
async def async_step_reauth(self, data: dict[str, str]) -> FlowResult:
"""Trigger a reauthentication flow."""
self._entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
assert entry is not None
self._entry = entry
self._host = data[CONF_HOST]
self._name = data[CONF_HOST]
self._name = str(data[CONF_HOST])
self._username = data[CONF_USERNAME]
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(self, user_input=None):
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle reauthorization flow."""
errors = {}

View file

@ -1,26 +1,29 @@
"""Constants for the AVM FRITZ!SmartHome integration."""
from __future__ import annotations
import logging
from typing import Final
ATTR_STATE_BATTERY_LOW = "battery_low"
ATTR_STATE_DEVICE_LOCKED = "device_locked"
ATTR_STATE_HOLIDAY_MODE = "holiday_mode"
ATTR_STATE_LOCKED = "locked"
ATTR_STATE_SUMMER_MODE = "summer_mode"
ATTR_STATE_WINDOW_OPEN = "window_open"
ATTR_STATE_BATTERY_LOW: Final = "battery_low"
ATTR_STATE_DEVICE_LOCKED: Final = "device_locked"
ATTR_STATE_HOLIDAY_MODE: Final = "holiday_mode"
ATTR_STATE_LOCKED: Final = "locked"
ATTR_STATE_SUMMER_MODE: Final = "summer_mode"
ATTR_STATE_WINDOW_OPEN: Final = "window_open"
ATTR_TEMPERATURE_UNIT = "temperature_unit"
ATTR_TEMPERATURE_UNIT: Final = "temperature_unit"
ATTR_TOTAL_CONSUMPTION = "total_consumption"
ATTR_TOTAL_CONSUMPTION_UNIT = "total_consumption_unit"
ATTR_TOTAL_CONSUMPTION: Final = "total_consumption"
ATTR_TOTAL_CONSUMPTION_UNIT: Final = "total_consumption_unit"
CONF_CONNECTIONS = "connections"
CONF_COORDINATOR = "coordinator"
CONF_CONNECTIONS: Final = "connections"
CONF_COORDINATOR: Final = "coordinator"
DEFAULT_HOST = "fritz.box"
DEFAULT_USERNAME = "admin"
DEFAULT_HOST: Final = "fritz.box"
DEFAULT_USERNAME: Final = "admin"
DOMAIN = "fritzbox"
DOMAIN: Final = "fritzbox"
LOGGER: logging.Logger = logging.getLogger(__package__)
LOGGER: Final[logging.Logger] = logging.getLogger(__package__)
PLATFORMS = ["binary_sensor", "climate", "switch", "sensor"]
PLATFORMS: Final[list[str]] = ["binary_sensor", "climate", "switch", "sensor"]

View file

@ -0,0 +1,43 @@
"""Models for the AVM FRITZ!SmartHome integration."""
from __future__ import annotations
from typing import TypedDict
class EntityInfo(TypedDict):
"""TypedDict for EntityInfo."""
name: str
entity_id: str
unit_of_measurement: str | None
device_class: str | None
class ClimateExtraAttributes(TypedDict, total=False):
"""TypedDict for climates extra attributes."""
battery_low: bool
device_locked: bool
locked: bool
battery_level: int
holiday_mode: bool
summer_mode: bool
window_open: bool
class SensorExtraAttributes(TypedDict):
"""TypedDict for sensors extra attributes."""
device_locked: bool
locked: bool
class SwitchExtraAttributes(TypedDict, total=False):
"""TypedDict for sensors extra attributes."""
device_locked: bool
locked: bool
total_consumption: str
total_consumption_unit: str
temperature: str
temperature_unit: str

View file

@ -1,4 +1,6 @@
"""Support for AVM FRITZ!SmartHome temperature sensor only devices."""
from __future__ import annotations
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@ -20,13 +22,14 @@ from .const import (
CONF_COORDINATOR,
DOMAIN as FRITZBOX_DOMAIN,
)
from .model import SensorExtraAttributes
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the FRITZ!SmartHome sensor from ConfigEntry."""
entities = []
entities: list[FritzBoxEntity] = []
coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR]
for ain, device in coordinator.data.items():
@ -69,23 +72,23 @@ class FritzBoxBatterySensor(FritzBoxEntity, SensorEntity):
"""The entity class for FRITZ!SmartHome sensors."""
@property
def state(self):
def state(self) -> int | None:
"""Return the state of the sensor."""
return self.device.battery_level
return self.device.battery_level # type: ignore [no-any-return]
class FritzBoxTempSensor(FritzBoxEntity, SensorEntity):
"""The entity class for FRITZ!SmartHome temperature sensors."""
@property
def state(self):
def state(self) -> float | None:
"""Return the state of the sensor."""
return self.device.temperature
return self.device.temperature # type: ignore [no-any-return]
@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> SensorExtraAttributes:
"""Return the state attributes of the device."""
attrs = {
attrs: SensorExtraAttributes = {
ATTR_STATE_DEVICE_LOCKED: self.device.device_lock,
ATTR_STATE_LOCKED: self.device.lock,
}

View file

@ -1,4 +1,8 @@
"""Support for AVM FRITZ!SmartHome switch devices."""
from __future__ import annotations
from typing import Any
from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@ -23,6 +27,7 @@ from .const import (
CONF_COORDINATOR,
DOMAIN as FRITZBOX_DOMAIN,
)
from .model import SwitchExtraAttributes
ATTR_TOTAL_CONSUMPTION_UNIT_VALUE = ENERGY_KILO_WATT_HOUR
@ -31,7 +36,7 @@ async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the FRITZ!SmartHome switch from ConfigEntry."""
entities = []
entities: list[FritzboxSwitch] = []
coordinator = hass.data[FRITZBOX_DOMAIN][entry.entry_id][CONF_COORDINATOR]
for ain, device in coordinator.data.items():
@ -58,31 +63,32 @@ class FritzboxSwitch(FritzBoxEntity, SwitchEntity):
"""The switch class for FRITZ!SmartHome switches."""
@property
def available(self):
def available(self) -> bool:
"""Return if switch is available."""
return self.device.present
return self.device.present # type: ignore [no-any-return]
@property
def is_on(self):
def is_on(self) -> bool:
"""Return true if the switch is on."""
return self.device.switch_state
return self.device.switch_state # type: ignore [no-any-return]
async def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self.hass.async_add_executor_job(self.device.set_switch_state_on)
await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self.hass.async_add_executor_job(self.device.set_switch_state_off)
await self.coordinator.async_refresh()
@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> SwitchExtraAttributes:
"""Return the state attributes of the device."""
attrs = {}
attrs[ATTR_STATE_DEVICE_LOCKED] = self.device.device_lock
attrs[ATTR_STATE_LOCKED] = self.device.lock
attrs: SwitchExtraAttributes = {
ATTR_STATE_DEVICE_LOCKED: self.device.device_lock,
ATTR_STATE_LOCKED: self.device.lock,
}
if self.device.has_powermeter:
attrs[
@ -99,6 +105,6 @@ class FritzboxSwitch(FritzBoxEntity, SwitchEntity):
return attrs
@property
def current_power_w(self):
def current_power_w(self) -> float:
"""Return the current power usage in W."""
return self.device.power / 1000
return self.device.power / 1000 # type: ignore [no-any-return]

View file

@ -294,7 +294,7 @@ ATTR_ID = "id"
ATTR_NAME: Final = "name"
# Contains one string or a list of strings, each being an entity id
ATTR_ENTITY_ID = "entity_id"
ATTR_ENTITY_ID: Final = "entity_id"
# Contains one string or a list of strings, each being an area id
ATTR_AREA_ID = "area_id"
@ -314,7 +314,7 @@ ATTR_IDENTIFIERS: Final = "identifiers"
ATTR_ICON = "icon"
# The unit of measurement if applicable
ATTR_UNIT_OF_MEASUREMENT = "unit_of_measurement"
ATTR_UNIT_OF_MEASUREMENT: Final = "unit_of_measurement"
CONF_UNIT_SYSTEM_METRIC: str = "metric"
CONF_UNIT_SYSTEM_IMPERIAL: str = "imperial"
@ -332,7 +332,7 @@ ATTR_MODEL: Final = "model"
ATTR_SW_VERSION: Final = "sw_version"
ATTR_BATTERY_CHARGING = "battery_charging"
ATTR_BATTERY_LEVEL = "battery_level"
ATTR_BATTERY_LEVEL: Final = "battery_level"
ATTR_WAKEUP = "wake_up_interval"
# For devices which support a code attribute
@ -379,10 +379,10 @@ ATTR_RESTORED = "restored"
ATTR_SUPPORTED_FEATURES = "supported_features"
# Class of device within its domain
ATTR_DEVICE_CLASS = "device_class"
ATTR_DEVICE_CLASS: Final = "device_class"
# Temperature attribute
ATTR_TEMPERATURE = "temperature"
ATTR_TEMPERATURE: Final = "temperature"
# #### UNITS OF MEASUREMENT ####
# Power units

View file

@ -154,6 +154,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.fritzbox.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.frontend.*]
check_untyped_defs = true
disallow_incomplete_defs = true
@ -781,9 +792,6 @@ ignore_errors = true
[mypy-homeassistant.components.freebox.*]
ignore_errors = true
[mypy-homeassistant.components.fritzbox.*]
ignore_errors = true
[mypy-homeassistant.components.garmin_connect.*]
ignore_errors = true

View file

@ -69,7 +69,6 @@ IGNORED_MODULES: Final[list[str]] = [
"homeassistant.components.fortios.*",
"homeassistant.components.foscam.*",
"homeassistant.components.freebox.*",
"homeassistant.components.fritzbox.*",
"homeassistant.components.garmin_connect.*",
"homeassistant.components.geniushub.*",
"homeassistant.components.glances.*",