Complete Huawei LTE type hints, make mypy check them (#41503)

This commit is contained in:
Ville Skyttä 2020-10-08 22:02:47 +03:00 committed by GitHub
parent 61a5bf5645
commit cf83c6bf00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 147 additions and 99 deletions

View file

@ -6,7 +6,7 @@ from functools import partial
import ipaddress
import logging
import time
from typing import Any, Callable, Dict, List, Set, Tuple
from typing import Any, Callable, Dict, List, Set, Tuple, cast
from urllib.parse import urlparse
import attr
@ -23,7 +23,9 @@ from url_normalize import url_normalize
import voluptuous as vol
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN
from homeassistant.components.device_tracker.const import (
DOMAIN as DEVICE_TRACKER_DOMAIN,
)
from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
@ -36,7 +38,7 @@ from homeassistant.const import (
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.core import CALLBACK_TYPE
from homeassistant.core import CALLBACK_TYPE, ServiceCall
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import (
config_validation as cv,
@ -50,7 +52,7 @@ from homeassistant.helpers.dispatcher import (
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from .const import (
ADMIN_SERVICES,
@ -158,7 +160,7 @@ class Router:
(KEY_DEVICE_INFORMATION, "DeviceName"),
):
try:
return self.data[key][item]
return cast(str, self.data[key][item])
except (KeyError, TypeError):
pass
return DEFAULT_DEVICE_NAME
@ -465,7 +467,7 @@ async def async_unload_entry(
return True
async def async_setup(hass: HomeAssistantType, config) -> bool:
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
"""Set up Huawei LTE component."""
# dicttoxml (used by huawei-lte-api) has uselessly verbose INFO level.
@ -473,13 +475,13 @@ async def async_setup(hass: HomeAssistantType, config) -> bool:
logging.getLogger("dicttoxml").setLevel(logging.WARNING)
# Arrange our YAML config to dict with normalized URLs as keys
domain_config = {}
domain_config: Dict[str, Dict[str, Any]] = {}
if DOMAIN not in hass.data:
hass.data[DOMAIN] = HuaweiLteData(hass_config=config, config=domain_config)
for router_config in config.get(DOMAIN, []):
domain_config[url_normalize(router_config.pop(CONF_URL))] = router_config
def service_handler(service) -> None:
def service_handler(service: ServiceCall) -> None:
"""Apply a service."""
url = service.data.get(CONF_URL)
routers = hass.data[DOMAIN].routers
@ -555,10 +557,12 @@ async def async_signal_options_update(
async_dispatcher_send(hass, UPDATE_OPTIONS_SIGNAL, config_entry)
async def async_migrate_entry(hass: HomeAssistantType, config_entry: ConfigEntry):
async def async_migrate_entry(
hass: HomeAssistantType, config_entry: ConfigEntry
) -> bool:
"""Migrate config entry to new version."""
if config_entry.version == 1:
options = config_entry.options
options = dict(config_entry.options)
recipient = options.get(CONF_RECIPIENT)
if isinstance(recipient, str):
options[CONF_RECIPIENT] = [x.strip() for x in recipient.split(",")]
@ -623,6 +627,7 @@ class HuaweiLteBaseEntity(Entity):
async def async_added_to_hass(self) -> None:
"""Connect to update signals."""
assert self.hass is not None
self._unsub_handlers.append(
async_dispatcher_connect(self.hass, UPDATE_SIGNAL, self._async_maybe_update)
)

View file

@ -1,7 +1,7 @@
"""Support for Huawei LTE binary sensors."""
import logging
from typing import List, Optional
from typing import Any, Callable, Dict, List, Optional
import attr
from huawei_lte_api.enums.cradle import ConnectionStatusEnum
@ -10,8 +10,10 @@ from homeassistant.components.binary_sensor import (
DOMAIN as BINARY_SENSOR_DOMAIN,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_URL
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
from . import HuaweiLteBaseEntity
from .const import (
@ -24,7 +26,11 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistantType,
config_entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up from config entry."""
router = hass.data[DOMAIN].routers[config_entry.data[CONF_URL]]
entities: List[Entity] = []
@ -107,9 +113,10 @@ class HuaweiLteMobileConnectionBinarySensor(HuaweiLteBaseBinarySensor):
@property
def is_on(self) -> bool:
"""Return whether the binary sensor is on."""
return self._raw_state and int(self._raw_state) in (
ConnectionStatusEnum.CONNECTED,
ConnectionStatusEnum.DISCONNECTING,
return bool(
self._raw_state
and int(self._raw_state)
in (ConnectionStatusEnum.CONNECTED, ConnectionStatusEnum.DISCONNECTING)
)
@property
@ -132,7 +139,7 @@ class HuaweiLteMobileConnectionBinarySensor(HuaweiLteBaseBinarySensor):
return True
@property
def device_state_attributes(self):
def device_state_attributes(self) -> Optional[Dict[str, Any]]:
"""Get additional attributes related to connection status."""
attributes = super().device_state_attributes
if self._raw_state in CONNECTION_STATE_ATTRIBUTES:

View file

@ -2,7 +2,7 @@
from collections import OrderedDict
import logging
from typing import Optional
from typing import Any, Dict, Optional
from urllib.parse import urlparse
from huawei_lte_api.AuthorizedConnection import AuthorizedConnection
@ -46,11 +46,17 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@staticmethod
@callback
def async_get_options_flow(config_entry):
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> "OptionsFlowHandler":
"""Get options flow."""
return OptionsFlowHandler(config_entry)
async def _async_show_user_form(self, user_input=None, errors=None):
async def _async_show_user_form(
self,
user_input: Optional[Dict[str, Any]] = None,
errors: Optional[Dict[str, str]] = None,
) -> Dict[str, Any]:
if user_input is None:
user_input = {}
return self.async_show_form(
@ -89,11 +95,13 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors=errors or {},
)
async def async_step_import(self, user_input=None):
async def async_step_import(
self, user_input: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Handle import initiated config flow."""
return await self.async_step_user(user_input)
def _already_configured(self, user_input):
def _already_configured(self, user_input: Dict[str, Any]) -> bool:
"""See if we already have a router matching user input configured."""
existing_urls = {
url_normalize(entry.data[CONF_URL], default_scheme="http")
@ -101,7 +109,9 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
}
return user_input[CONF_URL] in existing_urls
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Handle user initiated config flow."""
if user_input is None:
return await self._async_show_user_form()
@ -123,15 +133,18 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
conn: Optional[Connection] = None
def logout():
if hasattr(conn, "user"):
def logout() -> None:
if isinstance(conn, AuthorizedConnection):
try:
conn.user.logout()
except Exception: # pylint: disable=broad-except
_LOGGER.debug("Could not logout", exc_info=True)
def try_connect(username: Optional[str], password: Optional[str]) -> Connection:
def try_connect(user_input: Dict[str, Any]) -> Connection:
"""Try connecting with given credentials."""
username = user_input.get(CONF_USERNAME)
password = user_input.get(CONF_PASSWORD)
conn: Connection
if username or password:
conn = AuthorizedConnection(
user_input[CONF_URL],
@ -178,12 +191,9 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
title = info.get("DeviceName")
return title or DEFAULT_DEVICE_NAME
username = user_input.get(CONF_USERNAME)
password = user_input.get(CONF_PASSWORD)
assert self.hass is not None
try:
conn = await self.hass.async_add_executor_job(
try_connect, username, password
)
conn = await self.hass.async_add_executor_job(try_connect, user_input)
except LoginErrorUsernameWrongException:
errors[CONF_USERNAME] = "incorrect_username"
except LoginErrorPasswordWrongException:
@ -215,7 +225,9 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(title=title, data=user_input)
async def async_step_ssdp(self, discovery_info):
async def async_step_ssdp( # type: ignore # mypy says signature incompatible with supertype, but it's the same?
self, discovery_info: Dict[str, Any]
) -> Dict[str, Any]:
"""Handle SSDP initiated config flow."""
await self.async_set_unique_id(discovery_info[ssdp.ATTR_UPNP_UDN])
self._abort_if_unique_id_configured()
@ -256,7 +268,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(self, user_input=None):
async def async_step_init(
self, user_input: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Handle options flow."""
# Recipients are persisted as a list, but handled as comma separated string in UI

View file

@ -2,21 +2,23 @@
import logging
import re
from typing import Any, Dict, List, Optional, Set
from typing import Any, Callable, Dict, List, Optional, Set, cast
import attr
from stringcase import snakecase
from homeassistant.components.device_tracker import (
from homeassistant.components.device_tracker.config_entry import ScannerEntity
from homeassistant.components.device_tracker.const import (
DOMAIN as DEVICE_TRACKER_DOMAIN,
SOURCE_TYPE_ROUTER,
)
from homeassistant.components.device_tracker.config_entry import ScannerEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_URL
from homeassistant.core import callback
from homeassistant.helpers import entity_registry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
from . import HuaweiLteBaseEntity
from .const import DOMAIN, KEY_WLAN_HOST_LIST, UPDATE_SIGNAL
@ -26,7 +28,11 @@ _LOGGER = logging.getLogger(__name__)
_DEVICE_SCAN = f"{DEVICE_TRACKER_DOMAIN}/device_scan"
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistantType,
config_entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up from config entry."""
# Grab hosts list once to examine whether the initial fetch has got some data for
@ -42,7 +48,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
# Initialize already tracked entities
tracked: Set[str] = set()
registry = await entity_registry.async_get_registry(hass)
known_entities: List[HuaweiLteScannerEntity] = []
known_entities: List[Entity] = []
for entity in registry.entities.values():
if (
entity.domain == DEVICE_TRACKER_DOMAIN
@ -73,7 +79,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
@callback
def async_add_new_entities(hass, router_url, async_add_entities, tracked):
def async_add_new_entities(
hass: HomeAssistantType,
router_url: str,
async_add_entities: Callable[[List[Entity], bool], None],
tracked: Set[str],
) -> None:
"""Add new entities that are not already being tracked."""
router = hass.data[DOMAIN].routers[router_url]
try:
@ -104,7 +115,7 @@ def _better_snakecase(text: str) -> str:
lambda match: f"{match.group(1)}{match.group(2).lower()}{match.group(3)}",
text,
)
return snakecase(text)
return cast(str, snakecase(text))
@attr.s

View file

@ -2,13 +2,14 @@
import logging
import time
from typing import Any, List
from typing import Any, Dict, List, Optional
import attr
from huawei_lte_api.exceptions import ResponseErrorException
from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService
from homeassistant.const import CONF_RECIPIENT, CONF_URL
from homeassistant.helpers.typing import HomeAssistantType
from . import Router
from .const import DOMAIN
@ -16,7 +17,11 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
async def async_get_service(hass, config, discovery_info=None):
async def async_get_service(
hass: HomeAssistantType,
config: Dict[str, Any],
discovery_info: Optional[Dict[str, Any]] = None,
) -> Optional["HuaweiLteSmsNotificationService"]:
"""Get the notification service."""
if discovery_info is None:
return None

View file

@ -1,5 +1,6 @@
"""Support for Huawei LTE sensors."""
from bisect import bisect
import logging
import re
from typing import Callable, Dict, List, NamedTuple, Optional, Pattern, Tuple, Union
@ -10,6 +11,7 @@ from homeassistant.components.sensor import (
DEVICE_CLASS_SIGNAL_STRENGTH,
DOMAIN as SENSOR_DOMAIN,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_URL,
DATA_BYTES,
@ -18,7 +20,7 @@ from homeassistant.const import (
TIME_SECONDS,
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.typing import HomeAssistantType, StateType
from . import HuaweiLteBaseEntity
from .const import (
@ -66,11 +68,11 @@ SENSOR_META: Dict[Union[str, Tuple[str, str]], SensorMeta] = {
(KEY_DEVICE_SIGNAL, "dl_mcs"): SensorMeta(name="Downlink MCS"),
(KEY_DEVICE_SIGNAL, "dlbandwidth"): SensorMeta(
name="Downlink bandwidth",
icon=lambda x: (x is None or x < 8)
and "mdi:speedometer-slow"
or x < 15
and "mdi:speedometer-medium"
or "mdi:speedometer",
icon=lambda x: (
"mdi:speedometer-slow",
"mdi:speedometer-medium",
"mdi:speedometer",
)[bisect((8, 15), x if x is not None else -1000)],
),
(KEY_DEVICE_SIGNAL, "earfcn"): SensorMeta(name="EARFCN"),
(KEY_DEVICE_SIGNAL, "lac"): SensorMeta(name="LAC", icon="mdi:map-marker"),
@ -86,11 +88,11 @@ SENSOR_META: Dict[Union[str, Tuple[str, str]], SensorMeta] = {
(KEY_DEVICE_SIGNAL, "ul_mcs"): SensorMeta(name="Uplink MCS"),
(KEY_DEVICE_SIGNAL, "ulbandwidth"): SensorMeta(
name="Uplink bandwidth",
icon=lambda x: (x is None or x < 8)
and "mdi:speedometer-slow"
or x < 15
and "mdi:speedometer-medium"
or "mdi:speedometer",
icon=lambda x: (
"mdi:speedometer-slow",
"mdi:speedometer-medium",
"mdi:speedometer",
)[bisect((8, 15), x if x is not None else -1000)],
),
(KEY_DEVICE_SIGNAL, "mode"): SensorMeta(
name="Mode",
@ -101,77 +103,71 @@ SENSOR_META: Dict[Union[str, Tuple[str, str]], SensorMeta] = {
name="RSRQ",
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
# http://www.lte-anbieter.info/technik/rsrq.php
icon=lambda x: (x is None or x < -11)
and "mdi:signal-cellular-outline"
or x < -8
and "mdi:signal-cellular-1"
or x < -5
and "mdi:signal-cellular-2"
or "mdi:signal-cellular-3",
icon=lambda x: (
"mdi:signal-cellular-outline",
"mdi:signal-cellular-1",
"mdi:signal-cellular-2",
"mdi:signal-cellular-3",
)[bisect((-11, -8, -5), x if x is not None else -1000)],
enabled_default=True,
),
(KEY_DEVICE_SIGNAL, "rsrp"): SensorMeta(
name="RSRP",
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
# http://www.lte-anbieter.info/technik/rsrp.php
icon=lambda x: (x is None or x < -110)
and "mdi:signal-cellular-outline"
or x < -95
and "mdi:signal-cellular-1"
or x < -80
and "mdi:signal-cellular-2"
or "mdi:signal-cellular-3",
icon=lambda x: (
"mdi:signal-cellular-outline",
"mdi:signal-cellular-1",
"mdi:signal-cellular-2",
"mdi:signal-cellular-3",
)[bisect((-110, -95, -80), x if x is not None else -1000)],
enabled_default=True,
),
(KEY_DEVICE_SIGNAL, "rssi"): SensorMeta(
name="RSSI",
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
# https://eyesaas.com/wi-fi-signal-strength/
icon=lambda x: (x is None or x < -80)
and "mdi:signal-cellular-outline"
or x < -70
and "mdi:signal-cellular-1"
or x < -60
and "mdi:signal-cellular-2"
or "mdi:signal-cellular-3",
icon=lambda x: (
"mdi:signal-cellular-outline",
"mdi:signal-cellular-1",
"mdi:signal-cellular-2",
"mdi:signal-cellular-3",
)[bisect((-80, -70, -60), x if x is not None else -1000)],
enabled_default=True,
),
(KEY_DEVICE_SIGNAL, "sinr"): SensorMeta(
name="SINR",
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
# http://www.lte-anbieter.info/technik/sinr.php
icon=lambda x: (x is None or x < 0)
and "mdi:signal-cellular-outline"
or x < 5
and "mdi:signal-cellular-1"
or x < 10
and "mdi:signal-cellular-2"
or "mdi:signal-cellular-3",
icon=lambda x: (
"mdi:signal-cellular-outline",
"mdi:signal-cellular-1",
"mdi:signal-cellular-2",
"mdi:signal-cellular-3",
)[bisect((0, 5, 10), x if x is not None else -1000)],
enabled_default=True,
),
(KEY_DEVICE_SIGNAL, "rscp"): SensorMeta(
name="RSCP",
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
# https://wiki.teltonika.lt/view/RSCP
icon=lambda x: (x is None or x < -95)
and "mdi:signal-cellular-outline"
or x < -85
and "mdi:signal-cellular-1"
or x < -75
and "mdi:signal-cellular-2"
or "mdi:signal-cellular-3",
icon=lambda x: (
"mdi:signal-cellular-outline",
"mdi:signal-cellular-1",
"mdi:signal-cellular-2",
"mdi:signal-cellular-3",
)[bisect((-95, -85, -75), x if x is not None else -1000)],
),
(KEY_DEVICE_SIGNAL, "ecio"): SensorMeta(
name="EC/IO",
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
# https://wiki.teltonika.lt/view/EC/IO
icon=lambda x: (x is None or x < -20)
and "mdi:signal-cellular-outline"
or x < -10
and "mdi:signal-cellular-1"
or x < -6
and "mdi:signal-cellular-2"
or "mdi:signal-cellular-3",
icon=lambda x: (
"mdi:signal-cellular-outline",
"mdi:signal-cellular-1",
"mdi:signal-cellular-2",
"mdi:signal-cellular-3",
)[bisect((-20, -10, -6), x if x is not None else -1000)],
),
KEY_MONITORING_CHECK_NOTIFICATIONS: SensorMeta(
exclude=re.compile(
@ -322,7 +318,11 @@ SENSOR_META: Dict[Union[str, Tuple[str, str]], SensorMeta] = {
}
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistantType,
config_entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up from config entry."""
router = hass.data[DOMAIN].routers[config_entry.data[CONF_URL]]
sensors: List[Entity] = []
@ -346,7 +346,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_entities(sensors, True)
def format_default(value):
def format_default(value: StateType) -> Tuple[StateType, Optional[str]]:
"""Format value."""
unit = None
if value is not None:

View file

@ -1,7 +1,7 @@
"""Support for Huawei LTE switches."""
import logging
from typing import Any, List, Optional
from typing import Any, Callable, List, Optional
import attr
@ -10,8 +10,10 @@ from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SwitchEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_URL
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
from . import HuaweiLteBaseEntity
from .const import DOMAIN, KEY_DIALUP_MOBILE_DATASWITCH
@ -19,7 +21,11 @@ from .const import DOMAIN, KEY_DIALUP_MOBILE_DATASWITCH
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistantType,
config_entry: ConfigEntry,
async_add_entities: Callable[[List[Entity], bool], None],
) -> None:
"""Set up from config entry."""
router = hass.data[DOMAIN].routers[config_entry.data[CONF_URL]]
switches: List[Entity] = []

View file

@ -40,7 +40,7 @@ warn_incomplete_stub = true
warn_redundant_casts = true
warn_unused_configs = true
[mypy-homeassistant.block_async_io,homeassistant.bootstrap,homeassistant.components,homeassistant.config_entries,homeassistant.config,homeassistant.const,homeassistant.core,homeassistant.data_entry_flow,homeassistant.exceptions,homeassistant.__init__,homeassistant.loader,homeassistant.__main__,homeassistant.requirements,homeassistant.runner,homeassistant.setup,homeassistant.util,homeassistant.auth.*,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zone.*,homeassistant.helpers.*,homeassistant.scripts.*,homeassistant.util.*]
[mypy-homeassistant.block_async_io,homeassistant.bootstrap,homeassistant.components,homeassistant.config_entries,homeassistant.config,homeassistant.const,homeassistant.core,homeassistant.data_entry_flow,homeassistant.exceptions,homeassistant.__init__,homeassistant.loader,homeassistant.__main__,homeassistant.requirements,homeassistant.runner,homeassistant.setup,homeassistant.util,homeassistant.auth.*,homeassistant.components.automation.*,homeassistant.components.binary_sensor.*,homeassistant.components.calendar.*,homeassistant.components.cover.*,homeassistant.components.device_automation.*,homeassistant.components.frontend.*,homeassistant.components.geo_location.*,homeassistant.components.group.*,homeassistant.components.history.*,homeassistant.components.http.*,homeassistant.components.huawei_lte.*,homeassistant.components.image_processing.*,homeassistant.components.integration.*,homeassistant.components.light.*,homeassistant.components.lock.*,homeassistant.components.mailbox.*,homeassistant.components.media_player.*,homeassistant.components.notify.*,homeassistant.components.persistent_notification.*,homeassistant.components.proximity.*,homeassistant.components.remote.*,homeassistant.components.scene.*,homeassistant.components.sensor.*,homeassistant.components.sun.*,homeassistant.components.switch.*,homeassistant.components.systemmonitor.*,homeassistant.components.tts.*,homeassistant.components.vacuum.*,homeassistant.components.water_heater.*,homeassistant.components.weather.*,homeassistant.components.websocket_api.*,homeassistant.components.zone.*,homeassistant.helpers.*,homeassistant.scripts.*,homeassistant.util.*]
strict = true
ignore_errors = false
warn_unreachable = true