1
mirror of https://github.com/home-assistant/core synced 2024-08-31 05:57:13 +02:00

Add Tado weather support (#44807)

This commit is contained in:
Álvaro Fernández Rojas 2021-03-10 21:31:37 +01:00 committed by GitHub
parent 10535018cc
commit ff09643b33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 213 additions and 3 deletions

View File

@ -144,11 +144,13 @@ class TadoConnector:
self._fallback = fallback
self.home_id = None
self.home_name = None
self.tado = None
self.zones = None
self.devices = None
self.data = {
"device": {},
"weather": {},
"zone": {},
}
@ -164,7 +166,9 @@ class TadoConnector:
# Load zones and devices
self.zones = self.tado.getZones()
self.devices = self.tado.getDevices()
self.home_id = self.tado.getMe()["homes"][0]["id"]
tado_home = self.tado.getMe()["homes"][0]
self.home_id = tado_home["id"]
self.home_name = tado_home["name"]
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
@ -173,6 +177,11 @@ class TadoConnector:
self.update_sensor("device", device["shortSerialNo"])
for zone in self.zones:
self.update_sensor("zone", zone["id"])
self.data["weather"] = self.tado.getWeather()
dispatcher_send(
self.hass,
SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "weather", "data"),
)
def update_sensor(self, sensor_type, sensor):
"""Update the internal data from Tado."""

View File

@ -48,6 +48,21 @@ CONF_FALLBACK = "fallback"
DATA = "data"
UPDATE_TRACK = "update_track"
# Weather
CONDITIONS_MAP = {
"clear-night": {"NIGHT_CLEAR"},
"cloudy": {"CLOUDY", "CLOUDY_MOSTLY", "NIGHT_CLOUDY"},
"fog": {"FOGGY"},
"hail": {"HAIL", "RAIN_HAIL"},
"lightning": {"THUNDERSTORM"},
"partlycloudy": {"CLOUDY_PARTLY"},
"rainy": {"DRIZZLE", "RAIN", "SCATTERED_RAIN"},
"snowy": {"FREEZING", "SCATTERED_SNOW", "SNOW"},
"snowy-rainy": {"RAIN_SNOW", "SCATTERED_RAIN_SNOW"},
"sunny": {"SUN"},
"windy": {"WIND"},
}
# Types
TYPE_AIR_CONDITIONING = "AIR_CONDITIONING"
TYPE_HEATING = "HEATING"
@ -149,6 +164,7 @@ UNIQUE_ID = "unique_id"
DEFAULT_NAME = "Tado"
TADO_HOME = "Home"
TADO_ZONE = "Zone"
UPDATE_LISTENER = "update_listener"

View File

@ -1,7 +1,7 @@
"""Base class for Tado entity."""
from homeassistant.helpers.entity import Entity
from .const import DEFAULT_NAME, DOMAIN, TADO_ZONE
from .const import DEFAULT_NAME, DOMAIN, TADO_HOME, TADO_ZONE
class TadoDeviceEntity(Entity):
@ -32,6 +32,26 @@ class TadoDeviceEntity(Entity):
return False
class TadoHomeEntity(Entity):
"""Base implementation for Tado home."""
def __init__(self, tado):
"""Initialize a Tado home."""
super().__init__()
self.home_name = tado.home_name
self.home_id = tado.home_id
@property
def device_info(self):
"""Return the device_info of the device."""
return {
"identifiers": {(DOMAIN, self.home_id)},
"name": self.home_name,
"manufacturer": DEFAULT_NAME,
"model": TADO_HOME,
}
class TadoZoneEntity(Entity):
"""Base implementation for Tado zone."""

View File

@ -13,6 +13,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from .const import (
CONDITIONS_MAP,
DATA,
DOMAIN,
SIGNAL_TADO_UPDATE_RECEIVED,
@ -20,10 +21,16 @@ from .const import (
TYPE_HEATING,
TYPE_HOT_WATER,
)
from .entity import TadoZoneEntity
from .entity import TadoHomeEntity, TadoZoneEntity
_LOGGER = logging.getLogger(__name__)
HOME_SENSORS = {
"outdoor temperature",
"solar percentage",
"weather condition",
}
ZONE_SENSORS = {
TYPE_HEATING: [
"temperature",
@ -41,6 +48,14 @@ ZONE_SENSORS = {
}
def format_condition(condition: str) -> str:
"""Return condition from dict CONDITIONS_MAP."""
for key, value in CONDITIONS_MAP.items():
if condition in value:
return key
return condition
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
):
@ -50,6 +65,9 @@ async def async_setup_entry(
zones = tado.zones
entities = []
# Create home sensors
entities.extend([TadoHomeSensor(tado, variable) for variable in HOME_SENSORS])
# Create zone sensors
for zone in zones:
zone_type = zone["type"]
@ -68,6 +86,111 @@ async def async_setup_entry(
async_add_entities(entities, True)
class TadoHomeSensor(TadoHomeEntity, Entity):
"""Representation of a Tado Sensor."""
def __init__(self, tado, home_variable):
"""Initialize of the Tado Sensor."""
super().__init__(tado)
self._tado = tado
self.home_variable = home_variable
self._unique_id = f"{home_variable} {tado.home_id}"
self._state = None
self._state_attributes = None
self._tado_weather_data = self._tado.data["weather"]
async def async_added_to_hass(self):
"""Register for sensor updates."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
SIGNAL_TADO_UPDATE_RECEIVED.format(
self._tado.home_id, "weather", "data"
),
self._async_update_callback,
)
)
self._async_update_home_data()
@property
def unique_id(self):
"""Return the unique id."""
return self._unique_id
@property
def name(self):
"""Return the name of the sensor."""
return f"{self._tado.home_name} {self.home_variable}"
@property
def state(self):
"""Return the state of the sensor."""
return self._state
@property
def device_state_attributes(self):
"""Return the state attributes."""
return self._state_attributes
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
if self.home_variable == "temperature":
return TEMP_CELSIUS
if self.home_variable == "solar percentage":
return PERCENTAGE
if self.home_variable == "weather condition":
return None
@property
def device_class(self):
"""Return the device class."""
if self.home_variable == "outdoor temperature":
return DEVICE_CLASS_TEMPERATURE
return None
@callback
def _async_update_callback(self):
"""Update and write state."""
self._async_update_home_data()
self.async_write_ha_state()
@callback
def _async_update_home_data(self):
"""Handle update callbacks."""
try:
self._tado_weather_data = self._tado.data["weather"]
except KeyError:
return
if self.home_variable == "outdoor temperature":
self._state = self.hass.config.units.temperature(
self._tado_weather_data["outsideTemperature"]["celsius"],
TEMP_CELSIUS,
)
self._state_attributes = {
"time": self._tado_weather_data["outsideTemperature"]["timestamp"],
}
elif self.home_variable == "solar percentage":
self._state = self._tado_weather_data["solarIntensity"]["percentage"]
self._state_attributes = {
"time": self._tado_weather_data["solarIntensity"]["timestamp"],
}
elif self.home_variable == "weather condition":
self._state = format_condition(
self._tado_weather_data["weatherState"]["value"]
)
self._state_attributes = {
"time": self._tado_weather_data["weatherState"]["timestamp"]
}
class TadoZoneSensor(TadoZoneEntity, Entity):
"""Representation of a tado Sensor."""

View File

@ -21,6 +21,21 @@ async def test_air_con_create_sensors(hass):
assert state.state == "60.9"
async def test_home_create_sensors(hass):
"""Test creation of home sensors."""
await async_init_integration(hass)
state = hass.states.get("sensor.home_name_outdoor_temperature")
assert state.state == "7.46"
state = hass.states.get("sensor.home_name_solar_percentage")
assert state.state == "2.1"
state = hass.states.get("sensor.home_name_weather_condition")
assert state.state == "fog"
async def test_heater_create_sensors(hass):
"""Test creation of heater sensors."""

View File

@ -18,6 +18,7 @@ async def async_init_integration(
token_fixture = "tado/token.json"
devices_fixture = "tado/devices.json"
me_fixture = "tado/me.json"
weather_fixture = "tado/weather.json"
zones_fixture = "tado/zones.json"
# WR1 Device
@ -55,6 +56,10 @@ async def async_init_integration(
"https://my.tado.com/api/v2/me",
text=load_fixture(me_fixture),
)
m.get(
"https://my.tado.com/api/v2/homes/1/weather",
text=load_fixture(weather_fixture),
)
m.get(
"https://my.tado.com/api/v2/homes/1/devices",
text=load_fixture(devices_fixture),

22
tests/fixtures/tado/weather.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"outsideTemperature": {
"celsius": 7.46,
"fahrenheit": 45.43,
"precision": {
"celsius": 0.01,
"fahrenheit": 0.01
},
"timestamp": "2020-12-22T08:13:13.652Z",
"type": "TEMPERATURE"
},
"solarIntensity": {
"percentage": 2.1,
"timestamp": "2020-12-22T08:13:13.652Z",
"type": "PERCENTAGE"
},
"weatherState": {
"timestamp": "2020-12-22T08:13:13.652Z",
"type": "WEATHER_STATE",
"value": "FOGGY"
}
}