mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Refactor Tado to use runtime_data (#121373)
This commit is contained in:
parent
1fefd396b9
commit
fb8eeac563
@ -1,22 +1,19 @@
|
|||||||
"""Support for the (unofficial) Tado API."""
|
"""Support for the (unofficial) Tado API."""
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from dataclasses import dataclass
|
||||||
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from PyTado.interface import Tado
|
|
||||||
from requests import RequestException
|
|
||||||
import requests.exceptions
|
import requests.exceptions
|
||||||
|
|
||||||
from homeassistant.components.climate import PRESET_AWAY, PRESET_HOME
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util import Throttle
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_FALLBACK,
|
CONF_FALLBACK,
|
||||||
@ -24,18 +21,13 @@ from .const import (
|
|||||||
CONST_OVERLAY_TADO_DEFAULT,
|
CONST_OVERLAY_TADO_DEFAULT,
|
||||||
CONST_OVERLAY_TADO_MODE,
|
CONST_OVERLAY_TADO_MODE,
|
||||||
CONST_OVERLAY_TADO_OPTIONS,
|
CONST_OVERLAY_TADO_OPTIONS,
|
||||||
DATA,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
INSIDE_TEMPERATURE_MEASUREMENT,
|
|
||||||
PRESET_AUTO,
|
|
||||||
SIGNAL_TADO_MOBILE_DEVICE_UPDATE_RECEIVED,
|
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED,
|
|
||||||
TEMP_OFFSET,
|
|
||||||
UPDATE_LISTENER,
|
UPDATE_LISTENER,
|
||||||
UPDATE_MOBILE_DEVICE_TRACK,
|
UPDATE_MOBILE_DEVICE_TRACK,
|
||||||
UPDATE_TRACK,
|
UPDATE_TRACK,
|
||||||
)
|
)
|
||||||
from .services import setup_services
|
from .services import setup_services
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -63,7 +55,20 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
type TadoConfigEntry = ConfigEntry[TadoRuntimeData]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TadoRuntimeData:
|
||||||
|
"""Dataclass for Tado runtime data."""
|
||||||
|
|
||||||
|
tadoconnector: TadoConnector
|
||||||
|
update_track: Any
|
||||||
|
update_mobile_device_track: Any
|
||||||
|
update_listener: Any
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: TadoConfigEntry) -> bool:
|
||||||
"""Set up Tado from a config entry."""
|
"""Set up Tado from a config entry."""
|
||||||
|
|
||||||
_async_import_options_from_data_if_missing(hass, entry)
|
_async_import_options_from_data_if_missing(hass, entry)
|
||||||
@ -108,13 +113,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
update_listener = entry.add_update_listener(_async_update_listener)
|
update_listener = entry.add_update_listener(_async_update_listener)
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
entry.runtime_data = TadoRuntimeData(
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
tadoconnector=tadoconnector,
|
||||||
DATA: tadoconnector,
|
update_track=update_track,
|
||||||
UPDATE_TRACK: update_track,
|
update_mobile_device_track=update_mobile_devices,
|
||||||
UPDATE_MOBILE_DEVICE_TRACK: update_mobile_devices,
|
update_listener=update_listener,
|
||||||
UPDATE_LISTENER: update_listener,
|
)
|
||||||
}
|
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
@ -155,301 +159,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
hass.data[DOMAIN].pop(entry.entry_id)
|
hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
|
||||||
|
|
||||||
class TadoConnector:
|
|
||||||
"""An object to store the Tado data."""
|
|
||||||
|
|
||||||
def __init__(self, hass, username, password, fallback):
|
|
||||||
"""Initialize Tado Connector."""
|
|
||||||
self.hass = hass
|
|
||||||
self._username = username
|
|
||||||
self._password = password
|
|
||||||
self._fallback = fallback
|
|
||||||
|
|
||||||
self.home_id = None
|
|
||||||
self.home_name = None
|
|
||||||
self.tado = None
|
|
||||||
self.zones = None
|
|
||||||
self.devices = None
|
|
||||||
self.data = {
|
|
||||||
"device": {},
|
|
||||||
"mobile_device": {},
|
|
||||||
"weather": {},
|
|
||||||
"geofence": {},
|
|
||||||
"zone": {},
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fallback(self):
|
|
||||||
"""Return fallback flag to Smart Schedule."""
|
|
||||||
return self._fallback
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
"""Connect to Tado and fetch the zones."""
|
|
||||||
self.tado = Tado(self._username, self._password)
|
|
||||||
# Load zones and devices
|
|
||||||
self.zones = self.tado.get_zones()
|
|
||||||
self.devices = self.tado.get_devices()
|
|
||||||
tado_home = self.tado.get_me()["homes"][0]
|
|
||||||
self.home_id = tado_home["id"]
|
|
||||||
self.home_name = tado_home["name"]
|
|
||||||
|
|
||||||
def get_mobile_devices(self):
|
|
||||||
"""Return the Tado mobile devices."""
|
|
||||||
return self.tado.get_mobile_devices()
|
|
||||||
|
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
|
||||||
def update(self):
|
|
||||||
"""Update the registered zones."""
|
|
||||||
self.update_devices()
|
|
||||||
self.update_mobile_devices()
|
|
||||||
self.update_zones()
|
|
||||||
self.update_home()
|
|
||||||
|
|
||||||
def update_mobile_devices(self) -> None:
|
|
||||||
"""Update the mobile devices."""
|
|
||||||
try:
|
|
||||||
mobile_devices = self.get_mobile_devices()
|
|
||||||
except RuntimeError:
|
|
||||||
_LOGGER.error("Unable to connect to Tado while updating mobile devices")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not mobile_devices:
|
|
||||||
_LOGGER.debug("No linked mobile devices found for home ID %s", self.home_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Errors are planned to be converted to exceptions
|
|
||||||
# in PyTado library, so this can be removed
|
|
||||||
if isinstance(mobile_devices, dict) and mobile_devices.get("errors"):
|
|
||||||
_LOGGER.error(
|
|
||||||
"Error for home ID %s while updating mobile devices: %s",
|
|
||||||
self.home_id,
|
|
||||||
mobile_devices["errors"],
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
for mobile_device in mobile_devices:
|
|
||||||
self.data["mobile_device"][mobile_device["id"]] = mobile_device
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Dispatching update to %s mobile device: %s",
|
|
||||||
self.home_id,
|
|
||||||
mobile_device,
|
|
||||||
)
|
|
||||||
|
|
||||||
dispatcher_send(
|
|
||||||
self.hass,
|
|
||||||
SIGNAL_TADO_MOBILE_DEVICE_UPDATE_RECEIVED.format(self.home_id),
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_devices(self):
|
|
||||||
"""Update the device data from Tado."""
|
|
||||||
try:
|
|
||||||
devices = self.tado.get_devices()
|
|
||||||
except RuntimeError:
|
|
||||||
_LOGGER.error("Unable to connect to Tado while updating devices")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not devices:
|
|
||||||
_LOGGER.debug("No linked devices found for home ID %s", self.home_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Errors are planned to be converted to exceptions
|
|
||||||
# in PyTado library, so this can be removed
|
|
||||||
if isinstance(devices, dict) and devices.get("errors"):
|
|
||||||
_LOGGER.error(
|
|
||||||
"Error for home ID %s while updating devices: %s",
|
|
||||||
self.home_id,
|
|
||||||
devices["errors"],
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
for device in devices:
|
|
||||||
device_short_serial_no = device["shortSerialNo"]
|
|
||||||
_LOGGER.debug("Updating device %s", device_short_serial_no)
|
|
||||||
try:
|
|
||||||
if (
|
|
||||||
INSIDE_TEMPERATURE_MEASUREMENT
|
|
||||||
in device["characteristics"]["capabilities"]
|
|
||||||
):
|
|
||||||
device[TEMP_OFFSET] = self.tado.get_device_info(
|
|
||||||
device_short_serial_no, TEMP_OFFSET
|
|
||||||
)
|
|
||||||
except RuntimeError:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Unable to connect to Tado while updating device %s",
|
|
||||||
device_short_serial_no,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.data["device"][device_short_serial_no] = device
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Dispatching update to %s device %s: %s",
|
|
||||||
self.home_id,
|
|
||||||
device_short_serial_no,
|
|
||||||
device,
|
|
||||||
)
|
|
||||||
dispatcher_send(
|
|
||||||
self.hass,
|
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED.format(
|
|
||||||
self.home_id, "device", device_short_serial_no
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_zones(self):
|
|
||||||
"""Update the zone data from Tado."""
|
|
||||||
try:
|
|
||||||
zone_states = self.tado.get_zone_states()["zoneStates"]
|
|
||||||
except RuntimeError:
|
|
||||||
_LOGGER.error("Unable to connect to Tado while updating zones")
|
|
||||||
return
|
|
||||||
|
|
||||||
for zone in zone_states:
|
|
||||||
self.update_zone(int(zone))
|
|
||||||
|
|
||||||
def update_zone(self, zone_id):
|
|
||||||
"""Update the internal data from Tado."""
|
|
||||||
_LOGGER.debug("Updating zone %s", zone_id)
|
|
||||||
try:
|
|
||||||
data = self.tado.get_zone_state(zone_id)
|
|
||||||
except RuntimeError:
|
|
||||||
_LOGGER.error("Unable to connect to Tado while updating zone %s", zone_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.data["zone"][zone_id] = data
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Dispatching update to %s zone %s: %s",
|
|
||||||
self.home_id,
|
|
||||||
zone_id,
|
|
||||||
data,
|
|
||||||
)
|
|
||||||
dispatcher_send(
|
|
||||||
self.hass,
|
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "zone", zone_id),
|
|
||||||
)
|
|
||||||
|
|
||||||
def update_home(self):
|
|
||||||
"""Update the home data from Tado."""
|
|
||||||
try:
|
|
||||||
self.data["weather"] = self.tado.get_weather()
|
|
||||||
self.data["geofence"] = self.tado.get_home_state()
|
|
||||||
dispatcher_send(
|
|
||||||
self.hass,
|
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "home", "data"),
|
|
||||||
)
|
|
||||||
except RuntimeError:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Unable to connect to Tado while updating weather and geofence data"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
def get_capabilities(self, zone_id):
|
|
||||||
"""Return the capabilities of the devices."""
|
|
||||||
return self.tado.get_capabilities(zone_id)
|
|
||||||
|
|
||||||
def get_auto_geofencing_supported(self):
|
|
||||||
"""Return whether the Tado Home supports auto geofencing."""
|
|
||||||
return self.tado.get_auto_geofencing_supported()
|
|
||||||
|
|
||||||
def reset_zone_overlay(self, zone_id):
|
|
||||||
"""Reset the zone back to the default operation."""
|
|
||||||
self.tado.reset_zone_overlay(zone_id)
|
|
||||||
self.update_zone(zone_id)
|
|
||||||
|
|
||||||
def set_presence(
|
|
||||||
self,
|
|
||||||
presence=PRESET_HOME,
|
|
||||||
):
|
|
||||||
"""Set the presence to home, away or auto."""
|
|
||||||
if presence == PRESET_AWAY:
|
|
||||||
self.tado.set_away()
|
|
||||||
elif presence == PRESET_HOME:
|
|
||||||
self.tado.set_home()
|
|
||||||
elif presence == PRESET_AUTO:
|
|
||||||
self.tado.set_auto()
|
|
||||||
|
|
||||||
# Update everything when changing modes
|
|
||||||
self.update_zones()
|
|
||||||
self.update_home()
|
|
||||||
|
|
||||||
def set_zone_overlay(
|
|
||||||
self,
|
|
||||||
zone_id=None,
|
|
||||||
overlay_mode=None,
|
|
||||||
temperature=None,
|
|
||||||
duration=None,
|
|
||||||
device_type="HEATING",
|
|
||||||
mode=None,
|
|
||||||
fan_speed=None,
|
|
||||||
swing=None,
|
|
||||||
fan_level=None,
|
|
||||||
vertical_swing=None,
|
|
||||||
horizontal_swing=None,
|
|
||||||
):
|
|
||||||
"""Set a zone overlay."""
|
|
||||||
_LOGGER.debug(
|
|
||||||
(
|
|
||||||
"Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s,"
|
|
||||||
" type=%s, mode=%s fan_speed=%s swing=%s fan_level=%s vertical_swing=%s horizontal_swing=%s"
|
|
||||||
),
|
|
||||||
zone_id,
|
|
||||||
overlay_mode,
|
|
||||||
temperature,
|
|
||||||
duration,
|
|
||||||
device_type,
|
|
||||||
mode,
|
|
||||||
fan_speed,
|
|
||||||
swing,
|
|
||||||
fan_level,
|
|
||||||
vertical_swing,
|
|
||||||
horizontal_swing,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.tado.set_zone_overlay(
|
|
||||||
zone_id,
|
|
||||||
overlay_mode,
|
|
||||||
temperature,
|
|
||||||
duration,
|
|
||||||
device_type,
|
|
||||||
"ON",
|
|
||||||
mode,
|
|
||||||
fan_speed=fan_speed,
|
|
||||||
swing=swing,
|
|
||||||
fan_level=fan_level,
|
|
||||||
vertical_swing=vertical_swing,
|
|
||||||
horizontal_swing=horizontal_swing,
|
|
||||||
)
|
|
||||||
|
|
||||||
except RequestException as exc:
|
|
||||||
_LOGGER.error("Could not set zone overlay: %s", exc)
|
|
||||||
|
|
||||||
self.update_zone(zone_id)
|
|
||||||
|
|
||||||
def set_zone_off(self, zone_id, overlay_mode, device_type="HEATING"):
|
|
||||||
"""Set a zone to off."""
|
|
||||||
try:
|
|
||||||
self.tado.set_zone_overlay(
|
|
||||||
zone_id, overlay_mode, None, None, device_type, "OFF"
|
|
||||||
)
|
|
||||||
except RequestException as exc:
|
|
||||||
_LOGGER.error("Could not set zone overlay: %s", exc)
|
|
||||||
|
|
||||||
self.update_zone(zone_id)
|
|
||||||
|
|
||||||
def set_temperature_offset(self, device_id, offset):
|
|
||||||
"""Set temperature offset of device."""
|
|
||||||
try:
|
|
||||||
self.tado.set_temp_offset(device_id, offset)
|
|
||||||
except RequestException as exc:
|
|
||||||
_LOGGER.error("Could not set temperature offset: %s", exc)
|
|
||||||
|
|
||||||
def set_meter_reading(self, reading: int) -> dict[str, str]:
|
|
||||||
"""Send meter reading to Tado."""
|
|
||||||
dt: str = datetime.now().strftime("%Y-%m-%d")
|
|
||||||
try:
|
|
||||||
return self.tado.set_eiq_meter_readings(date=dt, reading=reading)
|
|
||||||
except RequestException as exc:
|
|
||||||
raise HomeAssistantError("Could not set meter reading") from exc
|
|
||||||
|
@ -12,16 +12,13 @@ from homeassistant.components.binary_sensor import (
|
|||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant, 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 homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from . import TadoConnector
|
from . import TadoConfigEntry
|
||||||
from .const import (
|
from .const import (
|
||||||
DATA,
|
|
||||||
DOMAIN,
|
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED,
|
SIGNAL_TADO_UPDATE_RECEIVED,
|
||||||
TYPE_AIR_CONDITIONING,
|
TYPE_AIR_CONDITIONING,
|
||||||
TYPE_BATTERY,
|
TYPE_BATTERY,
|
||||||
@ -30,6 +27,7 @@ from .const import (
|
|||||||
TYPE_POWER,
|
TYPE_POWER,
|
||||||
)
|
)
|
||||||
from .entity import TadoDeviceEntity, TadoZoneEntity
|
from .entity import TadoDeviceEntity, TadoZoneEntity
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -68,9 +66,9 @@ OVERLAY_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
|
|||||||
key="overlay",
|
key="overlay",
|
||||||
translation_key="overlay",
|
translation_key="overlay",
|
||||||
state_fn=lambda data: data.overlay_active,
|
state_fn=lambda data: data.overlay_active,
|
||||||
attributes_fn=lambda data: {"termination": data.overlay_termination_type}
|
attributes_fn=lambda data: (
|
||||||
if data.overlay_active
|
{"termination": data.overlay_termination_type} if data.overlay_active else {}
|
||||||
else {},
|
),
|
||||||
device_class=BinarySensorDeviceClass.POWER,
|
device_class=BinarySensorDeviceClass.POWER,
|
||||||
)
|
)
|
||||||
OPEN_WINDOW_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
|
OPEN_WINDOW_ENTITY_DESCRIPTION = TadoBinarySensorEntityDescription(
|
||||||
@ -119,11 +117,11 @@ ZONE_SENSORS = {
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: TadoConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Tado sensor platform."""
|
"""Set up the Tado sensor platform."""
|
||||||
|
|
||||||
tado = hass.data[DOMAIN][entry.entry_id][DATA]
|
tado: TadoConnector = entry.runtime_data.tadoconnector
|
||||||
devices = tado.devices
|
devices = tado.devices
|
||||||
zones = tado.zones
|
zones = tado.zones
|
||||||
entities: list[BinarySensorEntity] = []
|
entities: list[BinarySensorEntity] = []
|
||||||
|
@ -22,7 +22,6 @@ from homeassistant.components.climate import (
|
|||||||
HVACAction,
|
HVACAction,
|
||||||
HVACMode,
|
HVACMode,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature
|
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
@ -30,7 +29,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import VolDictType
|
from homeassistant.helpers.typing import VolDictType
|
||||||
|
|
||||||
from . import TadoConnector
|
from . import TadoConfigEntry, TadoConnector
|
||||||
from .const import (
|
from .const import (
|
||||||
CONST_EXCLUSIVE_OVERLAY_GROUP,
|
CONST_EXCLUSIVE_OVERLAY_GROUP,
|
||||||
CONST_FAN_AUTO,
|
CONST_FAN_AUTO,
|
||||||
@ -42,7 +41,6 @@ from .const import (
|
|||||||
CONST_MODE_SMART_SCHEDULE,
|
CONST_MODE_SMART_SCHEDULE,
|
||||||
CONST_OVERLAY_MANUAL,
|
CONST_OVERLAY_MANUAL,
|
||||||
CONST_OVERLAY_TADO_OPTIONS,
|
CONST_OVERLAY_TADO_OPTIONS,
|
||||||
DATA,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
HA_TERMINATION_DURATION,
|
HA_TERMINATION_DURATION,
|
||||||
HA_TERMINATION_TYPE,
|
HA_TERMINATION_TYPE,
|
||||||
@ -100,11 +98,11 @@ CLIMATE_TEMP_OFFSET_SCHEMA: VolDictType = {
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: TadoConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Tado climate platform."""
|
"""Set up the Tado climate platform."""
|
||||||
|
|
||||||
tado = hass.data[DOMAIN][entry.entry_id][DATA]
|
tado: TadoConnector = entry.runtime_data.tadoconnector
|
||||||
entities = await hass.async_add_executor_job(_generate_entities, tado)
|
entities = await hass.async_add_executor_job(_generate_entities, tado)
|
||||||
|
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
@ -9,27 +9,27 @@ from homeassistant.components.device_tracker import (
|
|||||||
SourceType,
|
SourceType,
|
||||||
TrackerEntity,
|
TrackerEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
|
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import TadoConnector
|
from . import TadoConfigEntry
|
||||||
from .const import DATA, DOMAIN, SIGNAL_TADO_MOBILE_DEVICE_UPDATE_RECEIVED
|
from .const import DOMAIN, SIGNAL_TADO_MOBILE_DEVICE_UPDATE_RECEIVED
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: TadoConfigEntry,
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Tado device scannery entity."""
|
"""Set up the Tado device scannery entity."""
|
||||||
_LOGGER.debug("Setting up Tado device scanner entity")
|
_LOGGER.debug("Setting up Tado device scanner entity")
|
||||||
tado: TadoConnector = hass.data[DOMAIN][entry.entry_id][DATA]
|
tado: TadoConnector = entry.runtime_data.tadoconnector
|
||||||
tracked: set = set()
|
tracked: set = set()
|
||||||
|
|
||||||
# Fix non-string unique_id for device trackers
|
# Fix non-string unique_id for device trackers
|
||||||
|
@ -43,7 +43,7 @@ class TadoHomeEntity(Entity):
|
|||||||
self.home_id = tado.home_id
|
self.home_id = tado.home_id
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
configuration_url="https://app.tado.com",
|
configuration_url="https://app.tado.com",
|
||||||
identifiers={(DOMAIN, tado.home_id)},
|
identifiers={(DOMAIN, str(tado.home_id))},
|
||||||
manufacturer=DEFAULT_NAME,
|
manufacturer=DEFAULT_NAME,
|
||||||
model=TADO_HOME,
|
model=TADO_HOME,
|
||||||
name=tado.home_name,
|
name=tado.home_name,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
"""Helper methods for Tado."""
|
"""Helper methods for Tado."""
|
||||||
|
|
||||||
from . import TadoConnector
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONST_OVERLAY_TADO_DEFAULT,
|
CONST_OVERLAY_TADO_DEFAULT,
|
||||||
CONST_OVERLAY_TADO_MODE,
|
CONST_OVERLAY_TADO_MODE,
|
||||||
CONST_OVERLAY_TIMER,
|
CONST_OVERLAY_TIMER,
|
||||||
)
|
)
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
|
|
||||||
def decide_overlay_mode(
|
def decide_overlay_mode(
|
||||||
|
@ -13,18 +13,15 @@ from homeassistant.components.sensor import (
|
|||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import PERCENTAGE, UnitOfTemperature
|
from homeassistant.const import PERCENTAGE, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant, 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 homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from . import TadoConnector
|
from . import TadoConfigEntry
|
||||||
from .const import (
|
from .const import (
|
||||||
CONDITIONS_MAP,
|
CONDITIONS_MAP,
|
||||||
DATA,
|
|
||||||
DOMAIN,
|
|
||||||
SENSOR_DATA_CATEGORY_GEOFENCE,
|
SENSOR_DATA_CATEGORY_GEOFENCE,
|
||||||
SENSOR_DATA_CATEGORY_WEATHER,
|
SENSOR_DATA_CATEGORY_WEATHER,
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED,
|
SIGNAL_TADO_UPDATE_RECEIVED,
|
||||||
@ -33,6 +30,7 @@ from .const import (
|
|||||||
TYPE_HOT_WATER,
|
TYPE_HOT_WATER,
|
||||||
)
|
)
|
||||||
from .entity import TadoHomeEntity, TadoZoneEntity
|
from .entity import TadoHomeEntity, TadoZoneEntity
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -197,11 +195,11 @@ ZONE_SENSORS = {
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: TadoConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Tado sensor platform."""
|
"""Set up the Tado sensor platform."""
|
||||||
|
|
||||||
tado = hass.data[DOMAIN][entry.entry_id][DATA]
|
tado: TadoConnector = entry.runtime_data.tadoconnector
|
||||||
zones = tado.zones
|
zones = tado.zones
|
||||||
entities: list[SensorEntity] = []
|
entities: list[SensorEntity] = []
|
||||||
|
|
||||||
|
@ -5,17 +5,17 @@ import logging
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||||
from homeassistant.helpers import selector
|
from homeassistant.helpers import selector
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_MESSAGE,
|
ATTR_MESSAGE,
|
||||||
CONF_CONFIG_ENTRY,
|
CONF_CONFIG_ENTRY,
|
||||||
CONF_READING,
|
CONF_READING,
|
||||||
DATA,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_ADD_METER_READING,
|
SERVICE_ADD_METER_READING,
|
||||||
)
|
)
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
SCHEMA_ADD_METER_READING = vol.Schema(
|
SCHEMA_ADD_METER_READING = vol.Schema(
|
||||||
@ -40,7 +40,12 @@ def setup_services(hass: HomeAssistant) -> None:
|
|||||||
reading: int = call.data[CONF_READING]
|
reading: int = call.data[CONF_READING]
|
||||||
_LOGGER.debug("Add meter reading %s", reading)
|
_LOGGER.debug("Add meter reading %s", reading)
|
||||||
|
|
||||||
tadoconnector = hass.data[DOMAIN][entry_id][DATA]
|
entry = hass.config_entries.async_get_entry(entry_id)
|
||||||
|
if entry is None:
|
||||||
|
raise ServiceValidationError("Config entry not found")
|
||||||
|
|
||||||
|
tadoconnector: TadoConnector = entry.runtime_data.tadoconnector
|
||||||
|
|
||||||
response: dict = await hass.async_add_executor_job(
|
response: dict = await hass.async_add_executor_job(
|
||||||
tadoconnector.set_meter_reading, call.data[CONF_READING]
|
tadoconnector.set_meter_reading, call.data[CONF_READING]
|
||||||
)
|
)
|
||||||
|
332
homeassistant/components/tado/tado_connector.py
Normal file
332
homeassistant/components/tado/tado_connector.py
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
"""Tado Connector a class to store the data as an object."""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from PyTado.interface import Tado
|
||||||
|
from requests import RequestException
|
||||||
|
|
||||||
|
from homeassistant.components.climate import PRESET_AWAY, PRESET_HOME
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
INSIDE_TEMPERATURE_MEASUREMENT,
|
||||||
|
PRESET_AUTO,
|
||||||
|
SIGNAL_TADO_MOBILE_DEVICE_UPDATE_RECEIVED,
|
||||||
|
SIGNAL_TADO_UPDATE_RECEIVED,
|
||||||
|
TEMP_OFFSET,
|
||||||
|
)
|
||||||
|
|
||||||
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=4)
|
||||||
|
SCAN_INTERVAL = timedelta(minutes=5)
|
||||||
|
SCAN_MOBILE_DEVICE_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TadoConnector:
|
||||||
|
"""An object to store the Tado data."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, username: str, password: str, fallback: str
|
||||||
|
) -> None:
|
||||||
|
"""Initialize Tado Connector."""
|
||||||
|
self.hass = hass
|
||||||
|
self._username = username
|
||||||
|
self._password = password
|
||||||
|
self._fallback = fallback
|
||||||
|
|
||||||
|
self.home_id: int = 0
|
||||||
|
self.home_name = None
|
||||||
|
self.tado = None
|
||||||
|
self.zones: list[dict[Any, Any]] = []
|
||||||
|
self.devices: list[dict[Any, Any]] = []
|
||||||
|
self.data: dict[str, dict] = {
|
||||||
|
"device": {},
|
||||||
|
"mobile_device": {},
|
||||||
|
"weather": {},
|
||||||
|
"geofence": {},
|
||||||
|
"zone": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fallback(self):
|
||||||
|
"""Return fallback flag to Smart Schedule."""
|
||||||
|
return self._fallback
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
"""Connect to Tado and fetch the zones."""
|
||||||
|
self.tado = Tado(self._username, self._password)
|
||||||
|
# Load zones and devices
|
||||||
|
self.zones = self.tado.get_zones()
|
||||||
|
self.devices = self.tado.get_devices()
|
||||||
|
tado_home = self.tado.get_me()["homes"][0]
|
||||||
|
self.home_id = tado_home["id"]
|
||||||
|
self.home_name = tado_home["name"]
|
||||||
|
|
||||||
|
def get_mobile_devices(self):
|
||||||
|
"""Return the Tado mobile devices."""
|
||||||
|
return self.tado.get_mobile_devices()
|
||||||
|
|
||||||
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
def update(self):
|
||||||
|
"""Update the registered zones."""
|
||||||
|
self.update_devices()
|
||||||
|
self.update_mobile_devices()
|
||||||
|
self.update_zones()
|
||||||
|
self.update_home()
|
||||||
|
|
||||||
|
def update_mobile_devices(self) -> None:
|
||||||
|
"""Update the mobile devices."""
|
||||||
|
try:
|
||||||
|
mobile_devices = self.get_mobile_devices()
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error("Unable to connect to Tado while updating mobile devices")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not mobile_devices:
|
||||||
|
_LOGGER.debug("No linked mobile devices found for home ID %s", self.home_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Errors are planned to be converted to exceptions
|
||||||
|
# in PyTado library, so this can be removed
|
||||||
|
if isinstance(mobile_devices, dict) and mobile_devices.get("errors"):
|
||||||
|
_LOGGER.error(
|
||||||
|
"Error for home ID %s while updating mobile devices: %s",
|
||||||
|
self.home_id,
|
||||||
|
mobile_devices["errors"],
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
for mobile_device in mobile_devices:
|
||||||
|
self.data["mobile_device"][mobile_device["id"]] = mobile_device
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Dispatching update to %s mobile device: %s",
|
||||||
|
self.home_id,
|
||||||
|
mobile_device,
|
||||||
|
)
|
||||||
|
|
||||||
|
dispatcher_send(
|
||||||
|
self.hass,
|
||||||
|
SIGNAL_TADO_MOBILE_DEVICE_UPDATE_RECEIVED.format(self.home_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_devices(self):
|
||||||
|
"""Update the device data from Tado."""
|
||||||
|
try:
|
||||||
|
devices = self.tado.get_devices()
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error("Unable to connect to Tado while updating devices")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not devices:
|
||||||
|
_LOGGER.debug("No linked devices found for home ID %s", self.home_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Errors are planned to be converted to exceptions
|
||||||
|
# in PyTado library, so this can be removed
|
||||||
|
if isinstance(devices, dict) and devices.get("errors"):
|
||||||
|
_LOGGER.error(
|
||||||
|
"Error for home ID %s while updating devices: %s",
|
||||||
|
self.home_id,
|
||||||
|
devices["errors"],
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
device_short_serial_no = device["shortSerialNo"]
|
||||||
|
_LOGGER.debug("Updating device %s", device_short_serial_no)
|
||||||
|
try:
|
||||||
|
if (
|
||||||
|
INSIDE_TEMPERATURE_MEASUREMENT
|
||||||
|
in device["characteristics"]["capabilities"]
|
||||||
|
):
|
||||||
|
device[TEMP_OFFSET] = self.tado.get_device_info(
|
||||||
|
device_short_serial_no, TEMP_OFFSET
|
||||||
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Unable to connect to Tado while updating device %s",
|
||||||
|
device_short_serial_no,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.data["device"][device_short_serial_no] = device
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Dispatching update to %s device %s: %s",
|
||||||
|
self.home_id,
|
||||||
|
device_short_serial_no,
|
||||||
|
device,
|
||||||
|
)
|
||||||
|
dispatcher_send(
|
||||||
|
self.hass,
|
||||||
|
SIGNAL_TADO_UPDATE_RECEIVED.format(
|
||||||
|
self.home_id, "device", device_short_serial_no
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_zones(self):
|
||||||
|
"""Update the zone data from Tado."""
|
||||||
|
try:
|
||||||
|
zone_states = self.tado.get_zone_states()["zoneStates"]
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error("Unable to connect to Tado while updating zones")
|
||||||
|
return
|
||||||
|
|
||||||
|
for zone in zone_states:
|
||||||
|
self.update_zone(int(zone))
|
||||||
|
|
||||||
|
def update_zone(self, zone_id):
|
||||||
|
"""Update the internal data from Tado."""
|
||||||
|
_LOGGER.debug("Updating zone %s", zone_id)
|
||||||
|
try:
|
||||||
|
data = self.tado.get_zone_state(zone_id)
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error("Unable to connect to Tado while updating zone %s", zone_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.data["zone"][zone_id] = data
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Dispatching update to %s zone %s: %s",
|
||||||
|
self.home_id,
|
||||||
|
zone_id,
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
dispatcher_send(
|
||||||
|
self.hass,
|
||||||
|
SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "zone", zone_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_home(self):
|
||||||
|
"""Update the home data from Tado."""
|
||||||
|
try:
|
||||||
|
self.data["weather"] = self.tado.get_weather()
|
||||||
|
self.data["geofence"] = self.tado.get_home_state()
|
||||||
|
dispatcher_send(
|
||||||
|
self.hass,
|
||||||
|
SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "home", "data"),
|
||||||
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Unable to connect to Tado while updating weather and geofence data"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_capabilities(self, zone_id):
|
||||||
|
"""Return the capabilities of the devices."""
|
||||||
|
return self.tado.get_capabilities(zone_id)
|
||||||
|
|
||||||
|
def get_auto_geofencing_supported(self):
|
||||||
|
"""Return whether the Tado Home supports auto geofencing."""
|
||||||
|
return self.tado.get_auto_geofencing_supported()
|
||||||
|
|
||||||
|
def reset_zone_overlay(self, zone_id):
|
||||||
|
"""Reset the zone back to the default operation."""
|
||||||
|
self.tado.reset_zone_overlay(zone_id)
|
||||||
|
self.update_zone(zone_id)
|
||||||
|
|
||||||
|
def set_presence(
|
||||||
|
self,
|
||||||
|
presence=PRESET_HOME,
|
||||||
|
):
|
||||||
|
"""Set the presence to home, away or auto."""
|
||||||
|
if presence == PRESET_AWAY:
|
||||||
|
self.tado.set_away()
|
||||||
|
elif presence == PRESET_HOME:
|
||||||
|
self.tado.set_home()
|
||||||
|
elif presence == PRESET_AUTO:
|
||||||
|
self.tado.set_auto()
|
||||||
|
|
||||||
|
# Update everything when changing modes
|
||||||
|
self.update_zones()
|
||||||
|
self.update_home()
|
||||||
|
|
||||||
|
def set_zone_overlay(
|
||||||
|
self,
|
||||||
|
zone_id=None,
|
||||||
|
overlay_mode=None,
|
||||||
|
temperature=None,
|
||||||
|
duration=None,
|
||||||
|
device_type="HEATING",
|
||||||
|
mode=None,
|
||||||
|
fan_speed=None,
|
||||||
|
swing=None,
|
||||||
|
fan_level=None,
|
||||||
|
vertical_swing=None,
|
||||||
|
horizontal_swing=None,
|
||||||
|
):
|
||||||
|
"""Set a zone overlay."""
|
||||||
|
_LOGGER.debug(
|
||||||
|
(
|
||||||
|
"Set overlay for zone %s: overlay_mode=%s, temp=%s, duration=%s,"
|
||||||
|
" type=%s, mode=%s fan_speed=%s swing=%s fan_level=%s vertical_swing=%s horizontal_swing=%s"
|
||||||
|
),
|
||||||
|
zone_id,
|
||||||
|
overlay_mode,
|
||||||
|
temperature,
|
||||||
|
duration,
|
||||||
|
device_type,
|
||||||
|
mode,
|
||||||
|
fan_speed,
|
||||||
|
swing,
|
||||||
|
fan_level,
|
||||||
|
vertical_swing,
|
||||||
|
horizontal_swing,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.tado.set_zone_overlay(
|
||||||
|
zone_id,
|
||||||
|
overlay_mode,
|
||||||
|
temperature,
|
||||||
|
duration,
|
||||||
|
device_type,
|
||||||
|
"ON",
|
||||||
|
mode,
|
||||||
|
fan_speed=fan_speed,
|
||||||
|
swing=swing,
|
||||||
|
fan_level=fan_level,
|
||||||
|
vertical_swing=vertical_swing,
|
||||||
|
horizontal_swing=horizontal_swing,
|
||||||
|
)
|
||||||
|
|
||||||
|
except RequestException as exc:
|
||||||
|
_LOGGER.error("Could not set zone overlay: %s", exc)
|
||||||
|
|
||||||
|
self.update_zone(zone_id)
|
||||||
|
|
||||||
|
def set_zone_off(self, zone_id, overlay_mode, device_type="HEATING"):
|
||||||
|
"""Set a zone to off."""
|
||||||
|
try:
|
||||||
|
self.tado.set_zone_overlay(
|
||||||
|
zone_id, overlay_mode, None, None, device_type, "OFF"
|
||||||
|
)
|
||||||
|
except RequestException as exc:
|
||||||
|
_LOGGER.error("Could not set zone overlay: %s", exc)
|
||||||
|
|
||||||
|
self.update_zone(zone_id)
|
||||||
|
|
||||||
|
def set_temperature_offset(self, device_id, offset):
|
||||||
|
"""Set temperature offset of device."""
|
||||||
|
try:
|
||||||
|
self.tado.set_temp_offset(device_id, offset)
|
||||||
|
except RequestException as exc:
|
||||||
|
_LOGGER.error("Could not set temperature offset: %s", exc)
|
||||||
|
|
||||||
|
def set_meter_reading(self, reading: int) -> dict[str, Any]:
|
||||||
|
"""Send meter reading to Tado."""
|
||||||
|
dt: str = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
if self.tado is None:
|
||||||
|
raise HomeAssistantError("Tado client is not initialized")
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.tado.set_eiq_meter_readings(date=dt, reading=reading)
|
||||||
|
except RequestException as exc:
|
||||||
|
raise HomeAssistantError("Could not set meter reading") from exc
|
@ -9,7 +9,6 @@ from homeassistant.components.water_heater import (
|
|||||||
WaterHeaterEntity,
|
WaterHeaterEntity,
|
||||||
WaterHeaterEntityFeature,
|
WaterHeaterEntityFeature,
|
||||||
)
|
)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
@ -17,7 +16,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import VolDictType
|
from homeassistant.helpers.typing import VolDictType
|
||||||
|
|
||||||
from . import TadoConnector
|
from . import TadoConfigEntry
|
||||||
from .const import (
|
from .const import (
|
||||||
CONST_HVAC_HEAT,
|
CONST_HVAC_HEAT,
|
||||||
CONST_MODE_AUTO,
|
CONST_MODE_AUTO,
|
||||||
@ -27,14 +26,13 @@ from .const import (
|
|||||||
CONST_OVERLAY_MANUAL,
|
CONST_OVERLAY_MANUAL,
|
||||||
CONST_OVERLAY_TADO_MODE,
|
CONST_OVERLAY_TADO_MODE,
|
||||||
CONST_OVERLAY_TIMER,
|
CONST_OVERLAY_TIMER,
|
||||||
DATA,
|
|
||||||
DOMAIN,
|
|
||||||
SIGNAL_TADO_UPDATE_RECEIVED,
|
SIGNAL_TADO_UPDATE_RECEIVED,
|
||||||
TYPE_HOT_WATER,
|
TYPE_HOT_WATER,
|
||||||
)
|
)
|
||||||
from .entity import TadoZoneEntity
|
from .entity import TadoZoneEntity
|
||||||
from .helper import decide_duration, decide_overlay_mode
|
from .helper import decide_duration, decide_overlay_mode
|
||||||
from .repairs import manage_water_heater_fallback_issue
|
from .repairs import manage_water_heater_fallback_issue
|
||||||
|
from .tado_connector import TadoConnector
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -65,11 +63,11 @@ WATER_HEATER_TIMER_SCHEMA: VolDictType = {
|
|||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: TadoConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Tado water heater platform."""
|
"""Set up the Tado water heater platform."""
|
||||||
|
|
||||||
tado = hass.data[DOMAIN][entry.entry_id][DATA]
|
tado: TadoConnector = entry.runtime_data.tadoconnector
|
||||||
entities = await hass.async_add_executor_job(_generate_entities, tado)
|
entities = await hass.async_add_executor_job(_generate_entities, tado)
|
||||||
|
|
||||||
platform = entity_platform.async_get_current_platform()
|
platform = entity_platform.async_get_current_platform()
|
||||||
@ -95,7 +93,9 @@ def _generate_entities(tado: TadoConnector) -> list:
|
|||||||
|
|
||||||
for zone in tado.zones:
|
for zone in tado.zones:
|
||||||
if zone["type"] == TYPE_HOT_WATER:
|
if zone["type"] == TYPE_HOT_WATER:
|
||||||
entity = create_water_heater_entity(tado, zone["name"], zone["id"], zone)
|
entity = create_water_heater_entity(
|
||||||
|
tado, zone["name"], zone["id"], str(zone["name"])
|
||||||
|
)
|
||||||
entities.append(entity)
|
entities.append(entity)
|
||||||
|
|
||||||
return entities
|
return entities
|
||||||
|
Loading…
x
Reference in New Issue
Block a user