Add strict typing to Tractive integration (#56948)

* Strict typing

* Add few missing types

* Run hassfest

* Fix mypy errors

* Use List instead of list
This commit is contained in:
Maciej Bieniek 2021-10-03 09:13:12 +02:00 committed by GitHub
parent 1aeab65f56
commit f3c76fb859
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 166 additions and 134 deletions

View file

@ -112,6 +112,7 @@ homeassistant.components.tautulli.*
homeassistant.components.tcp.* homeassistant.components.tcp.*
homeassistant.components.tile.* homeassistant.components.tile.*
homeassistant.components.tplink.* homeassistant.components.tplink.*
homeassistant.components.tractive.*
homeassistant.components.tradfri.* homeassistant.components.tradfri.*
homeassistant.components.tts.* homeassistant.components.tts.*
homeassistant.components.upcloud.* homeassistant.components.upcloud.*

View file

@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio import asyncio
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import Any, Final, List, cast
import aiotractive import aiotractive
@ -15,7 +16,7 @@ from homeassistant.const import (
CONF_PASSWORD, CONF_PASSWORD,
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import Event, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
@ -36,10 +37,10 @@ from .const import (
TRACKER_POSITION_UPDATED, TRACKER_POSITION_UPDATED,
) )
PLATFORMS = ["binary_sensor", "device_tracker", "sensor", "switch"] PLATFORMS: Final = ["binary_sensor", "device_tracker", "sensor", "switch"]
_LOGGER = logging.getLogger(__name__) _LOGGER: Final = logging.getLogger(__name__)
@dataclass @dataclass
@ -92,7 +93,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.config_entries.async_setup_platforms(entry, PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS)
async def cancel_listen_task(_): async def cancel_listen_task(_: Event) -> None:
await tractive.unsubscribe() await tractive.unsubscribe()
entry.async_on_unload( entry.async_on_unload(
@ -102,13 +103,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True return True
async def _generate_trackables(client, trackable): async def _generate_trackables(
client: aiotractive.Tractive,
trackable: aiotractive.trackable_object.TrackableObject,
) -> Trackables | None:
"""Generate trackables.""" """Generate trackables."""
trackable = await trackable.details() trackable = await trackable.details()
# Check that the pet has tracker linked. # Check that the pet has tracker linked.
if not trackable["device_id"]: if not trackable["device_id"]:
return return None
tracker = client.tracker(trackable["device_id"]) tracker = client.tracker(trackable["device_id"])
@ -132,37 +136,44 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
class TractiveClient: class TractiveClient:
"""A Tractive client.""" """A Tractive client."""
def __init__(self, hass, client, user_id): def __init__(
self, hass: HomeAssistant, client: aiotractive.Tractive, user_id: str
) -> None:
"""Initialize the client.""" """Initialize the client."""
self._hass = hass self._hass = hass
self._client = client self._client = client
self._user_id = user_id self._user_id = user_id
self._listen_task = None self._listen_task: asyncio.Task | None = None
@property @property
def user_id(self): def user_id(self) -> str:
"""Return user id.""" """Return user id."""
return self._user_id return self._user_id
async def trackable_objects(self): async def trackable_objects(
self,
) -> list[aiotractive.trackable_object.TrackableObject]:
"""Get list of trackable objects.""" """Get list of trackable objects."""
return await self._client.trackable_objects() return cast(
List[aiotractive.trackable_object.TrackableObject],
await self._client.trackable_objects(),
)
def tracker(self, tracker_id): def tracker(self, tracker_id: str) -> aiotractive.tracker.Tracker:
"""Get tracker by id.""" """Get tracker by id."""
return self._client.tracker(tracker_id) return self._client.tracker(tracker_id)
def subscribe(self): def subscribe(self) -> None:
"""Start event listener coroutine.""" """Start event listener coroutine."""
self._listen_task = asyncio.create_task(self._listen()) self._listen_task = asyncio.create_task(self._listen())
async def unsubscribe(self): async def unsubscribe(self) -> None:
"""Stop event listener coroutine.""" """Stop event listener coroutine."""
if self._listen_task: if self._listen_task:
self._listen_task.cancel() self._listen_task.cancel()
await self._client.close() await self._client.close()
async def _listen(self): async def _listen(self) -> None:
server_was_unavailable = False server_was_unavailable = False
while True: while True:
try: try:
@ -191,7 +202,7 @@ class TractiveClient:
server_was_unavailable = True server_was_unavailable = True
continue continue
def _send_hardware_update(self, event): def _send_hardware_update(self, event: dict[str, Any]) -> None:
# Sometimes hardware event doesn't contain complete data. # Sometimes hardware event doesn't contain complete data.
payload = { payload = {
ATTR_BATTERY_LEVEL: event["hardware"]["battery_level"], ATTR_BATTERY_LEVEL: event["hardware"]["battery_level"],
@ -204,7 +215,7 @@ class TractiveClient:
TRACKER_HARDWARE_STATUS_UPDATED, event["tracker_id"], payload TRACKER_HARDWARE_STATUS_UPDATED, event["tracker_id"], payload
) )
def _send_activity_update(self, event): def _send_activity_update(self, event: dict[str, Any]) -> None:
payload = { payload = {
ATTR_MINUTES_ACTIVE: event["progress"]["achieved_minutes"], ATTR_MINUTES_ACTIVE: event["progress"]["achieved_minutes"],
ATTR_DAILY_GOAL: event["progress"]["goal_minutes"], ATTR_DAILY_GOAL: event["progress"]["goal_minutes"],
@ -213,7 +224,7 @@ class TractiveClient:
TRACKER_ACTIVITY_STATUS_UPDATED, event["pet_id"], payload TRACKER_ACTIVITY_STATUS_UPDATED, event["pet_id"], payload
) )
def _send_position_update(self, event): def _send_position_update(self, event: dict[str, Any]) -> None:
payload = { payload = {
"latitude": event["position"]["latlong"][0], "latitude": event["position"]["latlong"][0],
"longitude": event["position"]["latlong"][1], "longitude": event["position"]["latlong"][1],
@ -223,7 +234,9 @@ class TractiveClient:
TRACKER_POSITION_UPDATED, event["tracker_id"], payload TRACKER_POSITION_UPDATED, event["tracker_id"], payload
) )
def _dispatch_tracker_event(self, event_name, tracker_id, payload): def _dispatch_tracker_event(
self, event_name: str, tracker_id: str, payload: dict[str, Any]
) -> None:
async_dispatcher_send( async_dispatcher_send(
self._hass, self._hass,
f"{event_name}-{tracker_id}", f"{event_name}-{tracker_id}",

View file

@ -1,15 +1,20 @@
"""Support for Tractive binary sensors.""" """Support for Tractive binary sensors."""
from __future__ import annotations from __future__ import annotations
from typing import Any, Final
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_BATTERY_CHARGING,
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_BATTERY_CHARGING from homeassistant.const import ATTR_BATTERY_CHARGING
from homeassistant.core import callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import Trackables
from .const import ( from .const import (
CLIENT, CLIENT,
DOMAIN, DOMAIN,
@ -19,34 +24,36 @@ from .const import (
) )
from .entity import TractiveEntity from .entity import TractiveEntity
TRACKERS_WITH_BUILTIN_BATTERY = ("TRNJA4", "TRAXL1") TRACKERS_WITH_BUILTIN_BATTERY: Final = ("TRNJA4", "TRAXL1")
class TractiveBinarySensor(TractiveEntity, BinarySensorEntity): class TractiveBinarySensor(TractiveEntity, BinarySensorEntity):
"""Tractive sensor.""" """Tractive sensor."""
def __init__(self, user_id, trackable, tracker_details, unique_id, description): def __init__(
self, user_id: str, item: Trackables, description: BinarySensorEntityDescription
) -> None:
"""Initialize sensor entity.""" """Initialize sensor entity."""
super().__init__(user_id, trackable, tracker_details) super().__init__(user_id, item.trackable, item.tracker_details)
self._attr_name = f"{trackable['details']['name']} {description.name}" self._attr_name = f"{item.trackable['details']['name']} {description.name}"
self._attr_unique_id = unique_id self._attr_unique_id = f"{item.trackable['_id']}_{description.key}"
self.entity_description = description self.entity_description = description
@callback @callback
def handle_server_unavailable(self): def handle_server_unavailable(self) -> None:
"""Handle server unavailable.""" """Handle server unavailable."""
self._attr_available = False self._attr_available = False
self.async_write_ha_state() self.async_write_ha_state()
@callback @callback
def handle_hardware_status_update(self, event): def handle_hardware_status_update(self, event: dict[str, Any]) -> None:
"""Handle hardware status update.""" """Handle hardware status update."""
self._attr_is_on = event[self.entity_description.key] self._attr_is_on = event[self.entity_description.key]
self._attr_available = True self._attr_available = True
self.async_write_ha_state() self.async_write_ha_state()
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Handle entity which will be added.""" """Handle entity which will be added."""
self.async_on_remove( self.async_on_remove(
@ -66,31 +73,24 @@ class TractiveBinarySensor(TractiveEntity, BinarySensorEntity):
) )
SENSOR_TYPE = BinarySensorEntityDescription( SENSOR_TYPE: Final = BinarySensorEntityDescription(
key=ATTR_BATTERY_CHARGING, key=ATTR_BATTERY_CHARGING,
name="Battery Charging", name="Battery Charging",
device_class=DEVICE_CLASS_BATTERY_CHARGING, device_class=DEVICE_CLASS_BATTERY_CHARGING,
) )
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 Tractive device trackers.""" """Set up Tractive device trackers."""
client = hass.data[DOMAIN][entry.entry_id][CLIENT] client = hass.data[DOMAIN][entry.entry_id][CLIENT]
trackables = hass.data[DOMAIN][entry.entry_id][TRACKABLES] trackables = hass.data[DOMAIN][entry.entry_id][TRACKABLES]
entities = [] entities = [
TractiveBinarySensor(client.user_id, item, SENSOR_TYPE)
for item in trackables: for item in trackables
if item.tracker_details["model_number"] not in TRACKERS_WITH_BUILTIN_BATTERY: if item.tracker_details["model_number"] in TRACKERS_WITH_BUILTIN_BATTERY
continue ]
entities.append(
TractiveBinarySensor(
client.user_id,
item.trackable,
item.tracker_details,
f"{item.trackable['_id']}_{SENSOR_TYPE.key}",
SENSOR_TYPE,
)
)
async_add_entities(entities) async_add_entities(entities)

View file

@ -2,7 +2,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any from typing import Any, Final
import aiotractive import aiotractive
import voluptuous as vol import voluptuous as vol
@ -15,9 +15,9 @@ from homeassistant.exceptions import HomeAssistantError
from .const import DOMAIN from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER: Final = logging.getLogger(__name__)
USER_DATA_SCHEMA = vol.Schema( USER_DATA_SCHEMA: Final = vol.Schema(
{vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str} {vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str}
) )
@ -74,7 +74,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return await self.async_step_reauth_confirm() return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm( async def async_step_reauth_confirm(
self, user_input: dict[str, Any] = None self, user_input: dict[str, Any] | None = None
) -> FlowResult: ) -> FlowResult:
"""Dialog that informs the user that reauth is required.""" """Dialog that informs the user that reauth is required."""

View file

@ -1,22 +1,23 @@
"""Constants for the tractive integration.""" """Constants for the tractive integration."""
from datetime import timedelta from datetime import timedelta
from typing import Final
DOMAIN = "tractive" DOMAIN: Final = "tractive"
RECONNECT_INTERVAL = timedelta(seconds=10) RECONNECT_INTERVAL: Final = timedelta(seconds=10)
ATTR_DAILY_GOAL = "daily_goal" ATTR_DAILY_GOAL: Final = "daily_goal"
ATTR_BUZZER = "buzzer" ATTR_BUZZER: Final = "buzzer"
ATTR_LED = "led" ATTR_LED: Final = "led"
ATTR_LIVE_TRACKING = "live_tracking" ATTR_LIVE_TRACKING: Final = "live_tracking"
ATTR_MINUTES_ACTIVE = "minutes_active" ATTR_MINUTES_ACTIVE: Final = "minutes_active"
CLIENT = "client" CLIENT: Final = "client"
TRACKABLES = "trackables" TRACKABLES: Final = "trackables"
TRACKER_HARDWARE_STATUS_UPDATED = f"{DOMAIN}_tracker_hardware_status_updated" TRACKER_HARDWARE_STATUS_UPDATED: Final = f"{DOMAIN}_tracker_hardware_status_updated"
TRACKER_POSITION_UPDATED = f"{DOMAIN}_tracker_position_updated" TRACKER_POSITION_UPDATED: Final = f"{DOMAIN}_tracker_position_updated"
TRACKER_ACTIVITY_STATUS_UPDATED = f"{DOMAIN}_tracker_activity_updated" TRACKER_ACTIVITY_STATUS_UPDATED: Final = f"{DOMAIN}_tracker_activity_updated"
SERVER_UNAVAILABLE = f"{DOMAIN}_server_unavailable" SERVER_UNAVAILABLE: Final = f"{DOMAIN}_server_unavailable"

View file

@ -1,12 +1,16 @@
"""Support for Tractive device trackers.""" """Support for Tractive device trackers."""
from __future__ import annotations
import logging from typing import Any
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
from homeassistant.components.device_tracker.config_entry import TrackerEntity from homeassistant.components.device_tracker.config_entry import TrackerEntity
from homeassistant.core import callback from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import Trackables
from .const import ( from .const import (
CLIENT, CLIENT,
DOMAIN, DOMAIN,
@ -17,26 +21,15 @@ from .const import (
) )
from .entity import TractiveEntity from .entity import TractiveEntity
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
async def async_setup_entry(hass, entry, async_add_entities): hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Tractive device trackers.""" """Set up Tractive device trackers."""
client = hass.data[DOMAIN][entry.entry_id][CLIENT] client = hass.data[DOMAIN][entry.entry_id][CLIENT]
trackables = hass.data[DOMAIN][entry.entry_id][TRACKABLES] trackables = hass.data[DOMAIN][entry.entry_id][TRACKABLES]
entities = [] entities = [TractiveDeviceTracker(client.user_id, item) for item in trackables]
for item in trackables:
entities.append(
TractiveDeviceTracker(
client.user_id,
item.trackable,
item.tracker_details,
item.hw_info,
item.pos_report,
)
)
async_add_entities(entities) async_add_entities(entities)
@ -46,51 +39,51 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity):
_attr_icon = "mdi:paw" _attr_icon = "mdi:paw"
def __init__(self, user_id, trackable, tracker_details, hw_info, pos_report): def __init__(self, user_id: str, item: Trackables) -> None:
"""Initialize tracker entity.""" """Initialize tracker entity."""
super().__init__(user_id, trackable, tracker_details) super().__init__(user_id, item.trackable, item.tracker_details)
self._battery_level = hw_info["battery_level"] self._battery_level: int = item.hw_info["battery_level"]
self._latitude = pos_report["latlong"][0] self._latitude: float = item.pos_report["latlong"][0]
self._longitude = pos_report["latlong"][1] self._longitude: float = item.pos_report["latlong"][1]
self._accuracy = pos_report["pos_uncertainty"] self._accuracy: int = item.pos_report["pos_uncertainty"]
self._attr_name = f"{self._tracker_id} {trackable['details']['name']}" self._attr_name = f"{self._tracker_id} {item.trackable['details']['name']}"
self._attr_unique_id = trackable["_id"] self._attr_unique_id = item.trackable["_id"]
@property @property
def source_type(self): def source_type(self) -> str:
"""Return the source type, eg gps or router, of the device.""" """Return the source type, eg gps or router, of the device."""
return SOURCE_TYPE_GPS return SOURCE_TYPE_GPS
@property @property
def latitude(self): def latitude(self) -> float:
"""Return latitude value of the device.""" """Return latitude value of the device."""
return self._latitude return self._latitude
@property @property
def longitude(self): def longitude(self) -> float:
"""Return longitude value of the device.""" """Return longitude value of the device."""
return self._longitude return self._longitude
@property @property
def location_accuracy(self): def location_accuracy(self) -> int:
"""Return the gps accuracy of the device.""" """Return the gps accuracy of the device."""
return self._accuracy return self._accuracy
@property @property
def battery_level(self): def battery_level(self) -> int:
"""Return the battery level of the device.""" """Return the battery level of the device."""
return self._battery_level return self._battery_level
@callback @callback
def _handle_hardware_status_update(self, event): def _handle_hardware_status_update(self, event: dict[str, Any]) -> None:
self._battery_level = event["battery_level"] self._battery_level = event["battery_level"]
self._attr_available = True self._attr_available = True
self.async_write_ha_state() self.async_write_ha_state()
@callback @callback
def _handle_position_update(self, event): def _handle_position_update(self, event: dict[str, Any]) -> None:
self._latitude = event["latitude"] self._latitude = event["latitude"]
self._longitude = event["longitude"] self._longitude = event["longitude"]
self._accuracy = event["accuracy"] self._accuracy = event["accuracy"]
@ -98,15 +91,11 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity):
self.async_write_ha_state() self.async_write_ha_state()
@callback @callback
def _handle_server_unavailable(self): def _handle_server_unavailable(self) -> None:
self._latitude = None
self._longitude = None
self._accuracy = None
self._battery_level = None
self._attr_available = False self._attr_available = False
self.async_write_ha_state() self.async_write_ha_state()
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Handle entity which will be added.""" """Handle entity which will be added."""
self.async_on_remove( self.async_on_remove(

View file

@ -1,4 +1,7 @@
"""A entity class for Tractive integration.""" """A entity class for Tractive integration."""
from __future__ import annotations
from typing import Any
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -8,7 +11,9 @@ from .const import DOMAIN
class TractiveEntity(Entity): class TractiveEntity(Entity):
"""Tractive entity class.""" """Tractive entity class."""
def __init__(self, user_id, trackable, tracker_details): def __init__(
self, user_id: str, trackable: dict[str, Any], tracker_details: dict[str, Any]
) -> None:
"""Initialize tracker entity.""" """Initialize tracker entity."""
self._attr_device_info = { self._attr_device_info = {
"identifiers": {(DOMAIN, tracker_details["_id"])}, "identifiers": {(DOMAIN, tracker_details["_id"])},

View file

@ -2,17 +2,21 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Final
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_BATTERY_LEVEL,
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
PERCENTAGE, PERCENTAGE,
TIME_MINUTES, TIME_MINUTES,
) )
from homeassistant.core import callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import Trackables
from .const import ( from .const import (
ATTR_DAILY_GOAL, ATTR_DAILY_GOAL,
ATTR_MINUTES_ACTIVE, ATTR_MINUTES_ACTIVE,
@ -27,25 +31,37 @@ from .entity import TractiveEntity
@dataclass @dataclass
class TractiveSensorEntityDescription(SensorEntityDescription): class TractiveRequiredKeysMixin:
"""Class describing Tractive sensor entities.""" """Mixin for required keys."""
entity_class: type[TractiveSensor] | None = None entity_class: type[TractiveSensor]
@dataclass
class TractiveSensorEntityDescription(
SensorEntityDescription, TractiveRequiredKeysMixin
):
"""Class describing Tractive sensor entities."""
class TractiveSensor(TractiveEntity, SensorEntity): class TractiveSensor(TractiveEntity, SensorEntity):
"""Tractive sensor.""" """Tractive sensor."""
def __init__(self, user_id, trackable, tracker_details, unique_id, description): def __init__(
self,
user_id: str,
item: Trackables,
description: TractiveSensorEntityDescription,
) -> None:
"""Initialize sensor entity.""" """Initialize sensor entity."""
super().__init__(user_id, trackable, tracker_details) super().__init__(user_id, item.trackable, item.tracker_details)
self._attr_name = f"{trackable['details']['name']} {description.name}" self._attr_name = f"{item.trackable['details']['name']} {description.name}"
self._attr_unique_id = unique_id self._attr_unique_id = f"{item.trackable['_id']}_{description.key}"
self.entity_description = description self.entity_description = description
@callback @callback
def handle_server_unavailable(self): def handle_server_unavailable(self) -> None:
"""Handle server unavailable.""" """Handle server unavailable."""
self._attr_available = False self._attr_available = False
self.async_write_ha_state() self.async_write_ha_state()
@ -55,13 +71,13 @@ class TractiveHardwareSensor(TractiveSensor):
"""Tractive hardware sensor.""" """Tractive hardware sensor."""
@callback @callback
def handle_hardware_status_update(self, event): def handle_hardware_status_update(self, event: dict[str, Any]) -> None:
"""Handle hardware status update.""" """Handle hardware status update."""
self._attr_native_value = event[self.entity_description.key] self._attr_native_value = event[self.entity_description.key]
self._attr_available = True self._attr_available = True
self.async_write_ha_state() self.async_write_ha_state()
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Handle entity which will be added.""" """Handle entity which will be added."""
self.async_on_remove( self.async_on_remove(
@ -85,13 +101,13 @@ class TractiveActivitySensor(TractiveSensor):
"""Tractive active sensor.""" """Tractive active sensor."""
@callback @callback
def handle_activity_status_update(self, event): def handle_activity_status_update(self, event: dict[str, Any]) -> None:
"""Handle activity status update.""" """Handle activity status update."""
self._attr_native_value = event[self.entity_description.key] self._attr_native_value = event[self.entity_description.key]
self._attr_available = True self._attr_available = True
self.async_write_ha_state() self.async_write_ha_state()
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Handle entity which will be added.""" """Handle entity which will be added."""
self.async_on_remove( self.async_on_remove(
@ -111,7 +127,7 @@ class TractiveActivitySensor(TractiveSensor):
) )
SENSOR_TYPES = ( SENSOR_TYPES: Final[tuple[TractiveSensorEntityDescription, ...]] = (
TractiveSensorEntityDescription( TractiveSensorEntityDescription(
key=ATTR_BATTERY_LEVEL, key=ATTR_BATTERY_LEVEL,
name="Battery Level", name="Battery Level",
@ -136,23 +152,17 @@ SENSOR_TYPES = (
) )
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 Tractive device trackers.""" """Set up Tractive device trackers."""
client = hass.data[DOMAIN][entry.entry_id][CLIENT] client = hass.data[DOMAIN][entry.entry_id][CLIENT]
trackables = hass.data[DOMAIN][entry.entry_id][TRACKABLES] trackables = hass.data[DOMAIN][entry.entry_id][TRACKABLES]
entities = [] entities = [
description.entity_class(client.user_id, item, description)
for item in trackables: for description in SENSOR_TYPES
for description in SENSOR_TYPES: for item in trackables
entities.append( ]
description.entity_class(
client.user_id,
item.trackable,
item.tracker_details,
f"{item.trackable['_id']}_{description.key}",
description,
)
)
async_add_entities(entities) async_add_entities(entities)

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import Any, Literal from typing import Any, Final, Literal, cast
from aiotractive.exceptions import TractiveError from aiotractive.exceptions import TractiveError
@ -26,7 +26,7 @@ from .const import (
) )
from .entity import TractiveEntity from .entity import TractiveEntity
_LOGGER = logging.getLogger(__name__) _LOGGER: Final = logging.getLogger(__name__)
@dataclass @dataclass
@ -43,7 +43,7 @@ class TractiveSwitchEntityDescription(
"""Class describing Tractive switch entities.""" """Class describing Tractive switch entities."""
SWITCH_TYPES: tuple[TractiveSwitchEntityDescription, ...] = ( SWITCH_TYPES: Final[tuple[TractiveSwitchEntityDescription, ...]] = (
TractiveSwitchEntityDescription( TractiveSwitchEntityDescription(
key=ATTR_BUZZER, key=ATTR_BUZZER,
name="Tracker Buzzer", name="Tracker Buzzer",
@ -162,12 +162,14 @@ class TractiveSwitch(TractiveEntity, SwitchEntity):
async def async_set_buzzer(self, active: bool) -> dict[str, Any]: async def async_set_buzzer(self, active: bool) -> dict[str, Any]:
"""Set the buzzer on/off.""" """Set the buzzer on/off."""
return await self._tracker.set_buzzer_active(active) return cast(dict[str, Any], await self._tracker.set_buzzer_active(active))
async def async_set_led(self, active: bool) -> dict[str, Any]: async def async_set_led(self, active: bool) -> dict[str, Any]:
"""Set the LED on/off.""" """Set the LED on/off."""
return await self._tracker.set_led_active(active) return cast(dict[str, Any], await self._tracker.set_led_active(active))
async def async_set_live_tracking(self, active: bool) -> dict[str, Any]: async def async_set_live_tracking(self, active: bool) -> dict[str, Any]:
"""Set the live tracking on/off.""" """Set the live tracking on/off."""
return await self._tracker.set_live_tracking_active(active) return cast(
dict[str, Any], await self._tracker.set_live_tracking_active(active)
)

View file

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