Add tado zone binary sensors (#44576)

These should be binary sensors.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
This commit is contained in:
Álvaro Fernández Rojas 2021-01-02 13:35:59 +01:00 committed by GitHub
parent 40cbe597be
commit 067f2d0098
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 197 additions and 86 deletions

View file

@ -4,14 +4,25 @@ import logging
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_CONNECTIVITY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_WINDOW,
BinarySensorEntity, BinarySensorEntity,
) )
from homeassistant.config_entries import ConfigEntry 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 .const import DATA, DOMAIN, SIGNAL_TADO_UPDATE_RECEIVED, TYPE_BATTERY, TYPE_POWER from .const import (
from .entity import TadoDeviceEntity DATA,
DOMAIN,
SIGNAL_TADO_UPDATE_RECEIVED,
TYPE_AIR_CONDITIONING,
TYPE_BATTERY,
TYPE_HEATING,
TYPE_HOT_WATER,
TYPE_POWER,
)
from .entity import TadoDeviceEntity, TadoZoneEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -25,6 +36,23 @@ DEVICE_SENSORS = {
], ],
} }
ZONE_SENSORS = {
TYPE_HEATING: [
"power",
"link",
"overlay",
"early start",
"open window",
],
TYPE_AIR_CONDITIONING: [
"power",
"link",
"overlay",
"open window",
],
TYPE_HOT_WATER: ["power", "link", "overlay"],
}
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities hass: HomeAssistant, entry: ConfigEntry, async_add_entities
@ -33,6 +61,7 @@ async def async_setup_entry(
tado = hass.data[DOMAIN][entry.entry_id][DATA] tado = hass.data[DOMAIN][entry.entry_id][DATA]
devices = tado.devices devices = tado.devices
zones = tado.zones
entities = [] entities = []
# Create device sensors # Create device sensors
@ -44,16 +73,30 @@ async def async_setup_entry(
entities.extend( entities.extend(
[ [
TadoDeviceSensor(tado, device, variable) TadoDeviceBinarySensor(tado, device, variable)
for variable in DEVICE_SENSORS[device_type] for variable in DEVICE_SENSORS[device_type]
] ]
) )
# Create zone sensors
for zone in zones:
zone_type = zone["type"]
if zone_type not in ZONE_SENSORS:
_LOGGER.warning("Unknown zone type skipped: %s", zone_type)
continue
entities.extend(
[
TadoZoneBinarySensor(tado, zone["name"], zone["id"], variable)
for variable in ZONE_SENSORS[zone_type]
]
)
if entities: if entities:
async_add_entities(entities, True) async_add_entities(entities, True)
class TadoDeviceSensor(TadoDeviceEntity, BinarySensorEntity): class TadoDeviceBinarySensor(TadoDeviceEntity, BinarySensorEntity):
"""Representation of a tado Sensor.""" """Representation of a tado Sensor."""
def __init__(self, tado, device_info, device_variable): def __init__(self, tado, device_info, device_variable):
@ -125,3 +168,95 @@ class TadoDeviceSensor(TadoDeviceEntity, BinarySensorEntity):
self._state = self._device_info.get("connectionState", {}).get( self._state = self._device_info.get("connectionState", {}).get(
"value", False "value", False
) )
class TadoZoneBinarySensor(TadoZoneEntity, BinarySensorEntity):
"""Representation of a tado Sensor."""
def __init__(self, tado, zone_name, zone_id, zone_variable):
"""Initialize of the Tado Sensor."""
self._tado = tado
super().__init__(zone_name, tado.home_id, zone_id)
self.zone_variable = zone_variable
self._unique_id = f"{zone_variable} {zone_id} {tado.home_id}"
self._state = None
self._tado_zone_data = None
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, "zone", self.zone_id
),
self._async_update_callback,
)
)
self._async_update_zone_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.zone_name} {self.zone_variable}"
@property
def is_on(self):
"""Return true if sensor is on."""
return self._state
@property
def device_class(self):
"""Return the class of this sensor."""
if self.zone_variable == "early start":
return DEVICE_CLASS_POWER
if self.zone_variable == "link":
return DEVICE_CLASS_CONNECTIVITY
if self.zone_variable == "open window":
return DEVICE_CLASS_WINDOW
if self.zone_variable == "overlay":
return DEVICE_CLASS_POWER
if self.zone_variable == "power":
return DEVICE_CLASS_POWER
return None
@callback
def _async_update_callback(self):
"""Update and write state."""
self._async_update_zone_data()
self.async_write_ha_state()
@callback
def _async_update_zone_data(self):
"""Handle update callbacks."""
try:
self._tado_zone_data = self._tado.data["zone"][self.zone_id]
except KeyError:
return
if self.zone_variable == "power":
self._state = self._tado_zone_data.power
elif self.zone_variable == "link":
self._state = self._tado_zone_data.link
elif self.zone_variable == "overlay":
self._state = self._tado_zone_data.overlay_active
elif self.zone_variable == "early start":
self._state = self._tado_zone_data.preparation
elif self.zone_variable == "open window":
self._state = bool(
self._tado_zone_data.open_window
or self._tado_zone_data.open_window_detected
)

View file

@ -40,6 +40,7 @@ class TadoZoneEntity(Entity):
super().__init__() super().__init__()
self._device_zone_id = f"{home_id}_{zone_id}" self._device_zone_id = f"{home_id}_{zone_id}"
self.zone_name = zone_name self.zone_name = zone_name
self.zone_id = zone_id
@property @property
def device_info(self): def device_info(self):

View file

@ -23,25 +23,16 @@ ZONE_SENSORS = {
TYPE_HEATING: [ TYPE_HEATING: [
"temperature", "temperature",
"humidity", "humidity",
"power",
"link",
"heating", "heating",
"tado mode", "tado mode",
"overlay",
"early start",
"open window",
], ],
TYPE_AIR_CONDITIONING: [ TYPE_AIR_CONDITIONING: [
"temperature", "temperature",
"humidity", "humidity",
"power",
"link",
"ac", "ac",
"tado mode", "tado mode",
"overlay",
"open window",
], ],
TYPE_HOT_WATER: ["power", "link", "tado mode", "overlay"], TYPE_HOT_WATER: ["tado mode"],
} }
@ -51,10 +42,10 @@ async def async_setup_entry(
"""Set up the Tado sensor platform.""" """Set up the Tado sensor platform."""
tado = hass.data[DOMAIN][entry.entry_id][DATA] tado = hass.data[DOMAIN][entry.entry_id][DATA]
# Create zone sensors
zones = tado.zones zones = tado.zones
entities = [] entities = []
# Create zone sensors
for zone in zones: for zone in zones:
zone_type = zone["type"] zone_type = zone["type"]
if zone_type not in ZONE_SENSORS: if zone_type not in ZONE_SENSORS:
@ -80,7 +71,6 @@ class TadoZoneSensor(TadoZoneEntity, Entity):
self._tado = tado self._tado = tado
super().__init__(zone_name, tado.home_id, zone_id) super().__init__(zone_name, tado.home_id, zone_id)
self.zone_id = zone_id
self.zone_variable = zone_variable self.zone_variable = zone_variable
self._unique_id = f"{zone_variable} {zone_id} {tado.home_id}" self._unique_id = f"{zone_variable} {zone_id} {tado.home_id}"
@ -172,12 +162,6 @@ class TadoZoneSensor(TadoZoneEntity, Entity):
"time": self._tado_zone_data.current_humidity_timestamp "time": self._tado_zone_data.current_humidity_timestamp
} }
elif self.zone_variable == "power":
self._state = self._tado_zone_data.power
elif self.zone_variable == "link":
self._state = self._tado_zone_data.link
elif self.zone_variable == "heating": elif self.zone_variable == "heating":
self._state = self._tado_zone_data.heating_power_percentage self._state = self._tado_zone_data.heating_power_percentage
self._state_attributes = { self._state_attributes = {
@ -188,26 +172,5 @@ class TadoZoneSensor(TadoZoneEntity, Entity):
self._state = self._tado_zone_data.ac_power self._state = self._tado_zone_data.ac_power
self._state_attributes = {"time": self._tado_zone_data.ac_power_timestamp} self._state_attributes = {"time": self._tado_zone_data.ac_power_timestamp}
elif self.zone_variable == "tado bridge status":
self._state = self._tado_zone_data.connection
elif self.zone_variable == "tado mode": elif self.zone_variable == "tado mode":
self._state = self._tado_zone_data.tado_mode self._state = self._tado_zone_data.tado_mode
elif self.zone_variable == "overlay":
self._state = self._tado_zone_data.overlay_active
self._state_attributes = (
{"termination": self._tado_zone_data.overlay_termination_type}
if self._tado_zone_data.overlay_active
else {}
)
elif self.zone_variable == "early start":
self._state = self._tado_zone_data.preparation
elif self.zone_variable == "open window":
self._state = bool(
self._tado_zone_data.open_window
or self._tado_zone_data.open_window_detected
)
self._state_attributes = self._tado_zone_data.open_window_attr

View file

@ -1,10 +1,64 @@
"""The sensor tests for the tado platform.""" """The sensor tests for the tado platform."""
from homeassistant.const import STATE_ON from homeassistant.const import STATE_OFF, STATE_ON
from .util import async_init_integration from .util import async_init_integration
async def test_air_con_create_binary_sensors(hass):
"""Test creation of aircon sensors."""
await async_init_integration(hass)
state = hass.states.get("binary_sensor.air_conditioning_power")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.air_conditioning_link")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.air_conditioning_overlay")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.air_conditioning_open_window")
assert state.state == STATE_OFF
async def test_heater_create_binary_sensors(hass):
"""Test creation of heater sensors."""
await async_init_integration(hass)
state = hass.states.get("binary_sensor.baseboard_heater_power")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.baseboard_heater_link")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.baseboard_heater_early_start")
assert state.state == STATE_OFF
state = hass.states.get("binary_sensor.baseboard_heater_overlay")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.baseboard_heater_open_window")
assert state.state == STATE_OFF
async def test_water_heater_create_binary_sensors(hass):
"""Test creation of water heater sensors."""
await async_init_integration(hass)
state = hass.states.get("binary_sensor.water_heater_link")
assert state.state == STATE_ON
state = hass.states.get("binary_sensor.water_heater_overlay")
assert state.state == STATE_OFF
state = hass.states.get("binary_sensor.water_heater_power")
assert state.state == STATE_ON
async def test_home_create_binary_sensors(hass): async def test_home_create_binary_sensors(hass):
"""Test creation of home binary sensors.""" """Test creation of home binary sensors."""

View file

@ -8,15 +8,6 @@ async def test_air_con_create_sensors(hass):
await async_init_integration(hass) await async_init_integration(hass)
state = hass.states.get("sensor.air_conditioning_power")
assert state.state == "ON"
state = hass.states.get("sensor.air_conditioning_link")
assert state.state == "ONLINE"
state = hass.states.get("sensor.air_conditioning_link")
assert state.state == "ONLINE"
state = hass.states.get("sensor.air_conditioning_tado_mode") state = hass.states.get("sensor.air_conditioning_tado_mode")
assert state.state == "HOME" assert state.state == "HOME"
@ -26,48 +17,24 @@ async def test_air_con_create_sensors(hass):
state = hass.states.get("sensor.air_conditioning_ac") state = hass.states.get("sensor.air_conditioning_ac")
assert state.state == "ON" assert state.state == "ON"
state = hass.states.get("sensor.air_conditioning_overlay")
assert state.state == "True"
state = hass.states.get("sensor.air_conditioning_humidity") state = hass.states.get("sensor.air_conditioning_humidity")
assert state.state == "60.9" assert state.state == "60.9"
state = hass.states.get("sensor.air_conditioning_open_window")
assert state.state == "False"
async def test_heater_create_sensors(hass): async def test_heater_create_sensors(hass):
"""Test creation of heater sensors.""" """Test creation of heater sensors."""
await async_init_integration(hass) await async_init_integration(hass)
state = hass.states.get("sensor.baseboard_heater_power")
assert state.state == "ON"
state = hass.states.get("sensor.baseboard_heater_link")
assert state.state == "ONLINE"
state = hass.states.get("sensor.baseboard_heater_link")
assert state.state == "ONLINE"
state = hass.states.get("sensor.baseboard_heater_tado_mode") state = hass.states.get("sensor.baseboard_heater_tado_mode")
assert state.state == "HOME" assert state.state == "HOME"
state = hass.states.get("sensor.baseboard_heater_temperature") state = hass.states.get("sensor.baseboard_heater_temperature")
assert state.state == "20.65" assert state.state == "20.65"
state = hass.states.get("sensor.baseboard_heater_early_start")
assert state.state == "False"
state = hass.states.get("sensor.baseboard_heater_overlay")
assert state.state == "True"
state = hass.states.get("sensor.baseboard_heater_humidity") state = hass.states.get("sensor.baseboard_heater_humidity")
assert state.state == "45.2" assert state.state == "45.2"
state = hass.states.get("sensor.baseboard_heater_open_window")
assert state.state == "False"
async def test_water_heater_create_sensors(hass): async def test_water_heater_create_sensors(hass):
"""Test creation of water heater sensors.""" """Test creation of water heater sensors."""
@ -76,12 +43,3 @@ async def test_water_heater_create_sensors(hass):
state = hass.states.get("sensor.water_heater_tado_mode") state = hass.states.get("sensor.water_heater_tado_mode")
assert state.state == "HOME" assert state.state == "HOME"
state = hass.states.get("sensor.water_heater_link")
assert state.state == "ONLINE"
state = hass.states.get("sensor.water_heater_overlay")
assert state.state == "False"
state = hass.states.get("sensor.water_heater_power")
assert state.state == "ON"