diff --git a/homeassistant/components/person/__init__.py b/homeassistant/components/person/__init__.py index f6444a869ee..3a7db248862 100644 --- a/homeassistant/components/person/__init__.py +++ b/homeassistant/components/person/__init__.py @@ -1,8 +1,9 @@ """Support for tracking people.""" from __future__ import annotations +from collections.abc import Callable import logging -from typing import Any +from typing import Any, Self import voluptuous as vol @@ -46,10 +47,13 @@ from homeassistant.helpers import ( service, ) from homeassistant.helpers.entity_component import EntityComponent -from homeassistant.helpers.event import async_track_state_change_event +from homeassistant.helpers.event import ( + EventStateChangedData, + async_track_state_change_event, +) from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.storage import Store -from homeassistant.helpers.typing import ConfigType +from homeassistant.helpers.typing import ConfigType, EventType from homeassistant.loader import bind_hass _LOGGER = logging.getLogger(__name__) @@ -112,7 +116,7 @@ async def async_create_person( @bind_hass async def async_add_user_device_tracker( hass: HomeAssistant, user_id: str, device_tracker_entity_id: str -): +) -> None: """Add a device tracker to a person linked to a user.""" coll: PersonStorageCollection = hass.data[DOMAIN][1] @@ -187,7 +191,9 @@ UPDATE_FIELDS = { class PersonStore(Store): """Person storage.""" - async def _async_migrate_func(self, old_major_version, old_minor_version, old_data): + async def _async_migrate_func( + self, old_major_version: int, old_minor_version: int, old_data: dict[str, Any] + ) -> dict[str, Any]: """Migrate to the new version. Migrate storage to use format of collection helper. @@ -281,14 +287,14 @@ class PersonStorageCollection(collection.DictStorageCollection): """Return a new updated data object.""" update_data = self.UPDATE_SCHEMA(update_data) - user_id = update_data.get(CONF_USER_ID) + user_id: str | None = update_data.get(CONF_USER_ID) if user_id is not None and user_id != item.get(CONF_USER_ID): await self._validate_user_id(user_id) return {**item, **update_data} - async def _validate_user_id(self, user_id): + async def _validate_user_id(self, user_id: str) -> None: """Validate the used user_id.""" if await self.hass.auth.async_get_user(user_id) is None: raise ValueError("User does not exist") @@ -402,32 +408,32 @@ class Person(collection.CollectionEntity, RestoreEntity): _attr_should_poll = False editable: bool - def __init__(self, config): + def __init__(self, config: dict[str, Any]) -> None: """Set up person.""" self._config = config - self._latitude = None - self._longitude = None - self._gps_accuracy = None - self._source = None - self._state = None - self._unsub_track_device = None + self._latitude: float | None = None + self._longitude: float | None = None + self._gps_accuracy: float | None = None + self._source: str | None = None + self._state: str | None = None + self._unsub_track_device: Callable[[], None] | None = None @classmethod - def from_storage(cls, config: ConfigType): + def from_storage(cls, config: ConfigType) -> Self: """Return entity instance initialized from storage.""" person = cls(config) person.editable = True return person @classmethod - def from_yaml(cls, config: ConfigType): + def from_yaml(cls, config: ConfigType) -> Self: """Return entity instance initialized from yaml.""" person = cls(config) person.editable = False return person @property - def name(self): + def name(self) -> str: """Return the name of the entity.""" return self._config[CONF_NAME] @@ -437,14 +443,14 @@ class Person(collection.CollectionEntity, RestoreEntity): return self._config.get(CONF_PICTURE) @property - def state(self): + def state(self) -> str | None: """Return the state of the person.""" return self._state @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the person.""" - data = {ATTR_EDITABLE: self.editable, ATTR_ID: self.unique_id} + data: dict[str, Any] = {ATTR_EDITABLE: self.editable, ATTR_ID: self.unique_id} if self._latitude is not None: data[ATTR_LATITUDE] = self._latitude if self._longitude is not None: @@ -459,16 +465,16 @@ class Person(collection.CollectionEntity, RestoreEntity): return data @property - def unique_id(self): + def unique_id(self) -> str: """Return a unique ID for the person.""" return self._config[CONF_ID] @property - def device_trackers(self): + def device_trackers(self) -> list[str]: """Return the device trackers for the person.""" return self._config[CONF_DEVICE_TRACKERS] - async def async_added_to_hass(self): + async def async_added_to_hass(self) -> None: """Register device trackers.""" await super().async_added_to_hass() if state := await self.async_get_last_state(): @@ -480,14 +486,14 @@ class Person(collection.CollectionEntity, RestoreEntity): else: # Wait for hass start to not have race between person # and device trackers finishing setup. - async def person_start_hass(now): + async def person_start_hass(_: Event) -> None: await self.async_update_config(self._config) self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_START, person_start_hass ) - async def async_update_config(self, config: ConfigType): + async def async_update_config(self, config: ConfigType) -> None: """Handle when the config is updated.""" self._config = config @@ -505,12 +511,14 @@ class Person(collection.CollectionEntity, RestoreEntity): self._update_state() @callback - def _async_handle_tracker_update(self, event): + def _async_handle_tracker_update( + self, event: EventType[EventStateChangedData] + ) -> None: """Handle the device tracker state changes.""" self._update_state() @callback - def _update_state(self): + def _update_state(self) -> None: """Update the state.""" latest_non_gps_home = latest_not_home = latest_gps = latest = None for entity_id in self._config[CONF_DEVICE_TRACKERS]: @@ -545,7 +553,7 @@ class Person(collection.CollectionEntity, RestoreEntity): self.async_write_ha_state() @callback - def _parse_source_state(self, state): + def _parse_source_state(self, state: State) -> None: """Parse source state and set person attributes. This is a device tracker state or the restored person state. @@ -570,7 +578,7 @@ def ws_list_person( ) -def _get_latest(prev: State | None, curr: State): +def _get_latest(prev: State | None, curr: State) -> State: """Get latest state.""" if prev is None or curr.last_updated > prev.last_updated: return curr