Add Tado weather support (#44807)
This commit is contained in:
parent
10535018cc
commit
ff09643b33
7 changed files with 213 additions and 3 deletions
|
@ -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."""
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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
22
tests/fixtures/tado/weather.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue