1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Disable mobile devices in tado (#160881)

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Erwin Douna
2026-02-12 23:56:01 +01:00
committed by GitHub
parent 877ad391f0
commit 4921f05189
12 changed files with 13 additions and 259 deletions

View File

@@ -33,18 +33,12 @@ from .const import (
DOMAIN,
TADO_BRIDGE_MODELS,
)
from .coordinator import (
TadoConfigEntry,
TadoData,
TadoDataUpdateCoordinator,
TadoMobileDeviceUpdateCoordinator,
)
from .coordinator import TadoConfigEntry, TadoDataUpdateCoordinator
from .services import async_setup_services
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.CLIMATE,
Platform.DEVICE_TRACKER,
Platform.SENSOR,
Platform.SWITCH,
Platform.WATER_HEATER,
@@ -103,9 +97,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool
coordinator = TadoDataUpdateCoordinator(hass, entry, tado)
await coordinator.async_config_entry_first_refresh()
mobile_coordinator = TadoMobileDeviceUpdateCoordinator(hass, entry, tado)
await mobile_coordinator.async_config_entry_first_refresh()
# Pre-register the bridge device to ensure it exists before other devices reference it
device_registry = dr.async_get(hass)
for device in coordinator.data["device"].values():
@@ -121,7 +112,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool
configuration_url=f"https://app.tado.com/en/main/settings/rooms-and-devices/device/{device['serialNo']}",
)
entry.runtime_data = TadoData(coordinator, mobile_coordinator)
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View File

@@ -120,7 +120,7 @@ async def async_setup_entry(
) -> None:
"""Set up the Tado sensor platform."""
tado = entry.runtime_data.coordinator
tado = entry.runtime_data
devices = tado.devices
zones = tado.zones
entities: list[BinarySensorEntity] = []

View File

@@ -105,7 +105,7 @@ async def async_setup_entry(
) -> None:
"""Set up the Tado climate platform."""
tado = entry.runtime_data.coordinator
tado = entry.runtime_data
entities = await _generate_entities(tado)
platform = entity_platform.async_get_current_platform()

View File

@@ -2,7 +2,6 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime, timedelta
import logging
from typing import Any
@@ -30,17 +29,8 @@ _LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=4)
SCAN_INTERVAL = timedelta(minutes=5)
SCAN_MOBILE_DEVICE_INTERVAL = timedelta(minutes=5)
type TadoConfigEntry = ConfigEntry[TadoData]
@dataclass
class TadoData:
"""Class to hold Tado data."""
coordinator: TadoDataUpdateCoordinator
mobile_coordinator: TadoMobileDeviceUpdateCoordinator
type TadoConfigEntry = ConfigEntry[TadoDataUpdateCoordinator]
class TadoDataUpdateCoordinator(DataUpdateCoordinator[dict[str, dict]]):
@@ -372,57 +362,3 @@ class TadoDataUpdateCoordinator(DataUpdateCoordinator[dict[str, dict]]):
)
except RequestException as exc:
raise HomeAssistantError(f"Error setting Tado child lock: {exc}") from exc
class TadoMobileDeviceUpdateCoordinator(DataUpdateCoordinator[dict[str, dict]]):
"""Class to manage the mobile devices from Tado via PyTado."""
config_entry: TadoConfigEntry
def __init__(
self,
hass: HomeAssistant,
config_entry: TadoConfigEntry,
tado: Tado,
) -> None:
"""Initialize the Tado data update coordinator."""
super().__init__(
hass,
_LOGGER,
config_entry=config_entry,
name=DOMAIN,
update_interval=SCAN_MOBILE_DEVICE_INTERVAL,
)
self._tado = tado
self.data: dict[str, dict] = {}
async def _async_update_data(self) -> dict[str, dict]:
"""Fetch the latest data from Tado."""
try:
mobile_devices = await self.hass.async_add_executor_job(
self._tado.get_mobile_devices
)
except RequestException as err:
_LOGGER.error("Error updating Tado mobile devices: %s", err)
raise UpdateFailed(f"Error updating Tado mobile devices: {err}") from err
mapped_mobile_devices: dict[str, dict] = {}
for mobile_device in mobile_devices:
mobile_device_id = mobile_device["id"]
_LOGGER.debug("Updating mobile device %s", mobile_device_id)
try:
mapped_mobile_devices[mobile_device_id] = mobile_device
_LOGGER.debug(
"Mobile device %s updated, with data: %s",
mobile_device_id,
mobile_device,
)
except RequestException:
_LOGGER.error(
"Unable to connect to Tado while updating mobile device %s",
mobile_device_id,
)
self.data["mobile_device"] = mapped_mobile_devices
return self.data

View File

@@ -1,140 +0,0 @@
"""Support for Tado Smart device trackers."""
from __future__ import annotations
import logging
from homeassistant.components.device_tracker import (
DOMAIN as DEVICE_TRACKER_DOMAIN,
TrackerEntity,
)
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import DOMAIN
from .coordinator import TadoConfigEntry, TadoMobileDeviceUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
entry: TadoConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Tado device scannery entity."""
_LOGGER.debug("Setting up Tado device scanner entity")
tado = entry.runtime_data.mobile_coordinator
tracked: set = set()
# Fix non-string unique_id for device trackers
# Can be removed in 2025.1
entity_registry = er.async_get(hass)
for device_key in tado.data["mobile_device"]:
if entity_id := entity_registry.async_get_entity_id(
DEVICE_TRACKER_DOMAIN, DOMAIN, device_key
):
entity_registry.async_update_entity(
entity_id, new_unique_id=str(device_key)
)
@callback
def update_devices() -> None:
"""Update the values of the devices."""
add_tracked_entities(hass, tado, async_add_entities, tracked)
update_devices()
@callback
def add_tracked_entities(
hass: HomeAssistant,
coordinator: TadoMobileDeviceUpdateCoordinator,
async_add_entities: AddConfigEntryEntitiesCallback,
tracked: set[str],
) -> None:
"""Add new tracker entities from Tado."""
_LOGGER.debug("Fetching Tado devices from API for (newly) tracked entities")
new_tracked = []
for device_key, device in coordinator.data["mobile_device"].items():
if device_key in tracked:
continue
_LOGGER.debug(
"Adding Tado device %s with deviceID %s", device["name"], device_key
)
new_tracked.append(
TadoDeviceTrackerEntity(device_key, device["name"], coordinator)
)
tracked.add(device_key)
async_add_entities(new_tracked)
class TadoDeviceTrackerEntity(CoordinatorEntity[DataUpdateCoordinator], TrackerEntity):
"""A Tado Device Tracker entity."""
_attr_available = False
def __init__(
self,
device_id: str,
device_name: str,
coordinator: TadoMobileDeviceUpdateCoordinator,
) -> None:
"""Initialize a Tado Device Tracker entity."""
super().__init__(coordinator)
self._attr_unique_id = str(device_id)
self._device_id = device_id
self._device_name = device_name
self._active = False
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self.update_state()
super()._handle_coordinator_update()
@callback
def update_state(self) -> None:
"""Update the Tado device."""
_LOGGER.debug(
"Updating Tado mobile device: %s (ID: %s)",
self._device_name,
self._device_id,
)
device = self.coordinator.data["mobile_device"][self._device_id]
self._attr_available = False
_LOGGER.debug(
"Tado device %s has geoTracking state %s",
device["name"],
device["settings"]["geoTrackingEnabled"],
)
if device["settings"]["geoTrackingEnabled"] is False:
return
self._attr_available = True
self._active = False
if device.get("location") is not None and device["location"]["atHome"]:
_LOGGER.debug("Tado device %s is at home", device["name"])
self._active = True
else:
_LOGGER.debug("Tado device %s is not at home", device["name"])
@property
def name(self) -> str:
"""Return the name of the device."""
return self._device_name
@property
def location_name(self) -> str:
"""Return the state of the device."""
return STATE_HOME if self._active else STATE_NOT_HOME

View File

@@ -14,7 +14,4 @@ async def async_get_config_entry_diagnostics(
) -> dict[str, Any]:
"""Return diagnostics for a Tado config entry."""
return {
"data": config_entry.runtime_data.coordinator.data,
"mobile_devices": config_entry.runtime_data.mobile_coordinator.data,
}
return {"data": config_entry.runtime_data.data}

View File

@@ -196,7 +196,7 @@ async def async_setup_entry(
) -> None:
"""Set up the Tado sensor platform."""
tado = entry.runtime_data.coordinator
tado = entry.runtime_data
zones = tado.zones
entities: list[SensorEntity] = []

View File

@@ -39,7 +39,7 @@ async def _add_meter_reading(call: ServiceCall) -> None:
call.hass, DOMAIN, call.data[CONF_CONFIG_ENTRY]
)
coordinator = entry.runtime_data.coordinator
coordinator = entry.runtime_data
response: dict = await coordinator.set_meter_reading(call.data[CONF_READING])
if ATTR_MESSAGE in response:

View File

@@ -20,14 +20,14 @@ async def async_setup_entry(
) -> None:
"""Set up the Tado switch platform."""
tado = entry.runtime_data.coordinator
tado = entry.runtime_data
entities: list[TadoChildLockSwitchEntity] = []
for zone in tado.zones:
zoneChildLockSupported = (
zone_child_lock_supported = (
len(zone["devices"]) > 0 and "childLockEnabled" in zone["devices"][0]
)
if not zoneChildLockSupported:
if not zone_child_lock_supported:
continue
entities.append(

View File

@@ -66,8 +66,7 @@ async def async_setup_entry(
) -> None:
"""Set up the Tado water heater platform."""
data = entry.runtime_data
coordinator = data.coordinator
coordinator = entry.runtime_data
entities = await _generate_entities(coordinator)
platform = entity_platform.async_get_current_platform()

View File

@@ -238,8 +238,7 @@ async def init_integration(hass: HomeAssistant):
await hass.async_block_till_done()
# For a first refresh
await entry.runtime_data.coordinator.async_refresh()
await entry.runtime_data.mobile_coordinator.async_refresh()
await entry.runtime_data.async_refresh()
await hass.async_block_till_done()
yield

View File

@@ -111,33 +111,5 @@
}),
}),
}),
'mobile_devices': dict({
'mobile_device': dict({
'123456': dict({
'deviceMetadata': dict({
'locale': 'nl',
'model': 'Samsung',
'osVersion': '14',
'platform': 'Android',
}),
'id': 123456,
'name': 'Home',
'settings': dict({
'geoTrackingEnabled': False,
'onDemandLogRetrievalEnabled': False,
'pushNotifications': dict({
'awayModeReminder': True,
'energyIqReminder': False,
'energySavingsReportReminder': True,
'homeModeReminder': True,
'incidentDetection': True,
'lowBatteryReminder': True,
'openWindowReminder': True,
}),
'specialOffersEnabled': False,
}),
}),
}),
}),
})
# ---