Enforce strict typing for Tile (#53410)

This commit is contained in:
Aaron Bach 2021-07-27 03:51:57 -06:00 committed by GitHub
parent 7103835d15
commit f4a7292f08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 79 additions and 30 deletions

View File

@ -95,6 +95,7 @@ homeassistant.components.synology_dsm.*
homeassistant.components.systemmonitor.*
homeassistant.components.tag.*
homeassistant.components.tcp.*
homeassistant.components.tile.*
homeassistant.components.tts.*
homeassistant.components.upcloud.*
homeassistant.components.uptime.*

View File

@ -1,15 +1,19 @@
"""The Tile component."""
from __future__ import annotations
from datetime import timedelta
from functools import partial
from pytile import async_login
from pytile.errors import InvalidAuthError, SessionExpiredError, TileError
from pytile.tile import Tile
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.entity_registry import async_migrate_entries
from homeassistant.helpers.entity_registry import RegistryEntry, async_migrate_entries
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util.async_ import gather_with_concurrency
@ -24,14 +28,14 @@ DEFAULT_UPDATE_INTERVAL = timedelta(minutes=2)
CONF_SHOW_INACTIVE = "show_inactive"
async def async_setup_entry(hass, entry):
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Tile as config entry."""
hass.data.setdefault(DOMAIN, {DATA_COORDINATOR: {}, DATA_TILE: {}})
hass.data[DOMAIN][DATA_COORDINATOR][entry.entry_id] = {}
hass.data[DOMAIN][DATA_TILE][entry.entry_id] = {}
@callback
def async_migrate_callback(entity_entry):
def async_migrate_callback(entity_entry: RegistryEntry) -> dict | None:
"""
Define a callback to migrate appropriate Tile entities to new unique IDs.
@ -39,7 +43,7 @@ async def async_setup_entry(hass, entry):
New: {username}_{uuid}
"""
if entity_entry.unique_id.startswith(entry.data[CONF_USERNAME]):
return
return None
new_unique_id = f"{entry.data[CONF_USERNAME]}_".join(
entity_entry.unique_id.split(f"{DOMAIN}_")
@ -71,10 +75,10 @@ async def async_setup_entry(hass, entry):
except TileError as err:
raise ConfigEntryNotReady("Error during integration setup") from err
async def async_update_tile(tile):
async def async_update_tile(tile: Tile) -> None:
"""Update the Tile."""
try:
return await tile.async_update()
await tile.async_update()
except SessionExpiredError:
LOGGER.info("Tile session expired; creating a new one")
await client.async_init()
@ -101,7 +105,7 @@ async def async_setup_entry(hass, entry):
return True
async def async_unload_entry(hass, entry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a Tile config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:

View File

@ -1,10 +1,15 @@
"""Config flow to configure the Tile integration."""
from __future__ import annotations
from typing import Any
from pytile import async_login
from pytile.errors import TileError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import aiohttp_client
from .const import DOMAIN
@ -15,23 +20,25 @@ class TileFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self):
def __init__(self) -> None:
"""Initialize the config flow."""
self.data_schema = vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
)
async def _show_form(self, errors=None):
async def _show_form(self, errors: dict[str, Any] | None = None) -> FlowResult:
"""Show the form to the user."""
return self.async_show_form(
step_id="user", data_schema=self.data_schema, errors=errors or {}
)
async def async_step_import(self, import_config):
async def async_step_import(self, import_config: dict[str, Any]) -> FlowResult:
"""Import a config entry from configuration.yaml."""
return await self.async_step_user(import_config)
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the start of the config flow."""
if not user_input:
return await self._show_form()

View File

@ -1,12 +1,23 @@
"""Support for Tile device trackers."""
from __future__ import annotations
from collections.abc import Awaitable
import logging
from typing import Any, Callable
from pytile.tile import Tile
from homeassistant.components.device_tracker.config_entry import TrackerEntity
from homeassistant.components.device_tracker.const import SOURCE_TYPE_GPS
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import ATTR_ATTRIBUTION, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import callback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from . import DATA_COORDINATOR, DATA_TILE, DOMAIN
@ -25,7 +36,9 @@ DEFAULT_ATTRIBUTION = "Data provided by Tile"
DEFAULT_ICON = "mdi:view-grid"
async def async_setup_entry(hass, entry, async_add_entities):
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Tile device trackers."""
async_add_entities(
[
@ -39,7 +52,12 @@ async def async_setup_entry(hass, entry, async_add_entities):
)
async def async_setup_scanner(hass, config, async_see, discovery_info=None):
async def async_setup_scanner(
hass: HomeAssistant,
config: ConfigType,
async_see: Callable[..., Awaitable[None]],
discovery_info: dict[str, Any] | None = None,
) -> bool:
"""Detect a legacy configuration and import it."""
hass.async_create_task(
hass.config_entries.flow.async_init(
@ -65,7 +83,9 @@ class TileDeviceTracker(CoordinatorEntity, TrackerEntity):
_attr_icon = DEFAULT_ICON
def __init__(self, entry, coordinator, tile):
def __init__(
self, entry: ConfigEntry, coordinator: DataUpdateCoordinator, tile: Tile
) -> None:
"""Initialize."""
super().__init__(coordinator)
@ -76,41 +96,47 @@ class TileDeviceTracker(CoordinatorEntity, TrackerEntity):
self._tile = tile
@property
def available(self):
def available(self) -> bool:
"""Return if entity is available."""
return super().available and not self._tile.dead
@property
def location_accuracy(self):
def location_accuracy(self) -> int:
"""Return the location accuracy of the device.
Value in meters.
"""
return self._tile.accuracy
if not self._tile.accuracy:
return super().location_accuracy
return int(self._tile.accuracy)
@property
def latitude(self) -> float:
def latitude(self) -> float | None:
"""Return latitude value of the device."""
if not self._tile.latitude:
return None
return self._tile.latitude
@property
def longitude(self) -> float:
def longitude(self) -> float | None:
"""Return longitude value of the device."""
if not self._tile.longitude:
return None
return self._tile.longitude
@property
def source_type(self):
def source_type(self) -> str:
"""Return the source type, eg gps or router, of the device."""
return SOURCE_TYPE_GPS
@callback
def _handle_coordinator_update(self):
def _handle_coordinator_update(self) -> None:
"""Respond to a DataUpdateCoordinator update."""
self._update_from_latest_data()
self.async_write_ha_state()
@callback
def _update_from_latest_data(self):
def _update_from_latest_data(self) -> None:
"""Update the entity from the latest data."""
self._attr_extra_state_attributes.update(
{
@ -122,7 +148,7 @@ class TileDeviceTracker(CoordinatorEntity, TrackerEntity):
}
)
async def async_added_to_hass(self):
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
self._update_from_latest_data()

View File

@ -3,7 +3,7 @@
"name": "Tile",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/tile",
"requirements": ["pytile==5.2.2"],
"requirements": ["pytile==5.2.3"],
"codeowners": ["@bachya"],
"iot_class": "cloud_polling"
}

View File

@ -1056,6 +1056,17 @@ no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.tile.*]
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.tts.*]
check_untyped_defs = true
disallow_incomplete_defs = true

View File

@ -1928,7 +1928,7 @@ python_opendata_transport==0.2.1
pythonegardia==1.0.40
# homeassistant.components.tile
pytile==5.2.2
pytile==5.2.3
# homeassistant.components.touchline
pytouchline==0.7

View File

@ -1071,7 +1071,7 @@ python-velbus==2.1.2
python_awair==0.2.1
# homeassistant.components.tile
pytile==5.2.2
pytile==5.2.3
# homeassistant.components.traccar
pytraccar==0.9.0