Centralizes Toon data, reducing API calls (#23988)

* Centralizes Toon data, reducing API calls

Fixes #21825

Signed-off-by: Franck Nijhof <frenck@addons.community>

* Fixes bad copy past action in services.yaml

Signed-off-by: Franck Nijhof <frenck@addons.community>

* Addresses review comments

Signed-off-by: Franck Nijhof <frenck@addons.community>

* 👕 Fixes too many blank lines

* Unsub dispatcher
pull/25010/head
Franck Nijhof 2019-07-09 14:18:51 +02:00 committed by Pascal Vizeli
parent f3e542542a
commit 3ce1049d21
6 changed files with 538 additions and 198 deletions

View File

@ -5,26 +5,59 @@ from functools import partial
import voluptuous as vol
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import (config_validation as cv,
device_registry as dr)
from homeassistant.core import callback
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
CONF_SCAN_INTERVAL,
)
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.dispatcher import (
dispatcher_send,
async_dispatcher_connect,
)
from . import config_flow # noqa pylint_disable=unused-import
from .const import (
CONF_CLIENT_ID, CONF_CLIENT_SECRET, CONF_DISPLAY, CONF_TENANT,
DATA_TOON_CLIENT, DATA_TOON_CONFIG, DOMAIN)
CONF_CLIENT_ID,
CONF_CLIENT_SECRET,
CONF_DISPLAY,
CONF_TENANT,
DATA_TOON_CLIENT,
DATA_TOON_CONFIG,
DATA_TOON_UPDATED,
DATA_TOON,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
# Validation of the user's configuration
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
}),
}, extra=vol.ALLOW_EXTRA)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Required(
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
): vol.All(cv.time_period, cv.positive_timedelta),
}
)
},
extra=vol.ALLOW_EXTRA,
)
SERVICE_SCHEMA = vol.Schema(
{vol.Optional(CONF_DISPLAY): cv.string}
)
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
@ -40,49 +73,119 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
return True
async def async_setup_entry(hass: HomeAssistantType,
entry: ConfigType) -> bool:
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigType
) -> bool:
"""Set up Toon from a config entry."""
from toonapilib import Toon
conf = hass.data.get(DATA_TOON_CONFIG)
toon = await hass.async_add_executor_job(partial(
Toon, entry.data[CONF_USERNAME], entry.data[CONF_PASSWORD],
conf[CONF_CLIENT_ID], conf[CONF_CLIENT_SECRET],
tenant_id=entry.data[CONF_TENANT],
display_common_name=entry.data[CONF_DISPLAY]))
toon = await hass.async_add_executor_job(
partial(
Toon,
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
conf[CONF_CLIENT_ID],
conf[CONF_CLIENT_SECRET],
tenant_id=entry.data[CONF_TENANT],
display_common_name=entry.data[CONF_DISPLAY],
)
)
hass.data.setdefault(DATA_TOON_CLIENT, {})[entry.entry_id] = toon
toon_data = ToonData(hass, entry, toon)
hass.data.setdefault(DATA_TOON, {})[entry.entry_id] = toon_data
async_track_time_interval(hass, toon_data.update, conf[CONF_SCAN_INTERVAL])
# Register device for the Meter Adapter, since it will have no entities.
device_registry = await dr.async_get_registry(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={
(DOMAIN, toon.agreement.id, 'meter_adapter'),
},
identifiers={(DOMAIN, toon.agreement.id, 'meter_adapter')},
manufacturer='Eneco',
name="Meter Adapter",
via_device=(DOMAIN, toon.agreement.id)
via_device=(DOMAIN, toon.agreement.id),
)
def update(call):
"""Service call to manually update the data."""
called_display = call.data.get(CONF_DISPLAY, None)
for toon_data in hass.data[DATA_TOON].values():
if (called_display and called_display == toon_data.display_name) \
or not called_display:
toon_data.update()
hass.services.async_register(
DOMAIN, "update", update, schema=SERVICE_SCHEMA
)
for component in 'binary_sensor', 'climate', 'sensor':
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component))
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True
class ToonData:
"""Communication class for interacting with toonapilib."""
def __init__(
self,
hass: HomeAssistantType,
entry: ConfigType,
toon
):
"""Initialize the Toon data object."""
self._hass = hass
self._toon = toon
self._entry = entry
self.agreement = toon.agreement
self.gas = toon.gas
self.power = toon.power
self.solar = toon.solar
self.temperature = toon.temperature
self.thermostat = toon.thermostat
self.thermostat_info = toon.thermostat_info
self.thermostat_state = toon.thermostat_state
@property
def display_name(self):
"""Return the display connected to."""
return self._entry.data[CONF_DISPLAY]
def update(self, now=None):
"""Update all Toon data and notify entities."""
# Ignore the TTL meganism from client library
# It causes a lots of issues, hence we take control over caching
self._toon._clear_cache() # noqa pylint: disable=W0212
# Gather data from client library (single API call)
self.gas = self._toon.gas
self.power = self._toon.power
self.solar = self._toon.solar
self.temperature = self._toon.temperature
self.thermostat = self._toon.thermostat
self.thermostat_info = self._toon.thermostat_info
self.thermostat_state = self._toon.thermostat_state
# Notify all entities
dispatcher_send(
self._hass, DATA_TOON_UPDATED, self._entry.data[CONF_DISPLAY]
)
class ToonEntity(Entity):
"""Defines a base Toon entity."""
def __init__(self, toon, name: str, icon: str) -> None:
def __init__(self, toon: ToonData, name: str, icon: str) -> None:
"""Initialize the Toon entity."""
self._name = name
self._state = None
self._icon = icon
self.toon = toon
self._unsub_dispatcher = None
@property
def name(self) -> str:
@ -94,6 +197,27 @@ class ToonEntity(Entity):
"""Return the mdi icon of the entity."""
return self._icon
@property
def should_poll(self) -> bool:
"""Return the polling requirement of the entity."""
return False
async def async_added_to_hass(self) -> None:
"""Connect to dispatcher listening for entity data notifications."""
self._unsub_dispatcher = async_dispatcher_connect(
self.hass, DATA_TOON_UPDATED, self._schedule_immediate_update
)
async def async_will_remove_from_hass(self) -> None:
"""Disconnect from update signal."""
self._unsub_dispatcher()
@callback
def _schedule_immediate_update(self, display_name: str) -> None:
"""Schedule an immediate update of the entity."""
if display_name == self.toon.display_name:
self.async_schedule_update_ha_state(True)
class ToonDisplayDeviceEntity(ToonEntity):
"""Defines a Toon display device entity."""
@ -105,9 +229,7 @@ class ToonDisplayDeviceEntity(ToonEntity):
model = agreement.display_hardware_version.rpartition('/')[0]
sw_version = agreement.display_software_version.rpartition('/')[-1]
return {
'identifiers': {
(DOMAIN, agreement.id),
},
'identifiers': {(DOMAIN, agreement.id)},
'name': 'Toon Display',
'manufacturer': 'Eneco',
'model': model,

View File

@ -1,6 +1,5 @@
"""Support for Toon binary sensors."""
from datetime import timedelta
import logging
from typing import Any
@ -8,62 +7,123 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import HomeAssistantType
from . import (ToonEntity, ToonDisplayDeviceEntity, ToonBoilerDeviceEntity,
ToonBoilerModuleDeviceEntity)
from .const import DATA_TOON_CLIENT, DOMAIN
from . import (
ToonData,
ToonEntity,
ToonDisplayDeviceEntity,
ToonBoilerDeviceEntity,
ToonBoilerModuleDeviceEntity,
)
from .const import DATA_TOON, DOMAIN
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
SCAN_INTERVAL = timedelta(seconds=300)
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry,
async_add_entities) -> None:
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
) -> None:
"""Set up a Toon binary sensor based on a config entry."""
toon = hass.data[DATA_TOON_CLIENT][entry.entry_id]
toon = hass.data[DATA_TOON][entry.entry_id]
sensors = [
ToonBoilerModuleBinarySensor(toon, 'thermostat_info',
'boiler_connected', None,
'Boiler Module Connection',
'mdi:check-network-outline',
'connectivity'),
ToonDisplayBinarySensor(toon, 'thermostat_info', 'active_state', 4,
"Toon Holiday Mode", 'mdi:airport', None),
ToonDisplayBinarySensor(toon, 'thermostat_info', 'next_program', None,
"Toon Program", 'mdi:calendar-clock', None),
ToonBoilerModuleBinarySensor(
toon,
'thermostat_info',
'boiler_connected',
None,
'Boiler Module Connection',
'mdi:check-network-outline',
'connectivity',
),
ToonDisplayBinarySensor(
toon,
'thermostat_info',
'active_state',
4,
"Toon Holiday Mode",
'mdi:airport',
None,
),
ToonDisplayBinarySensor(
toon,
'thermostat_info',
'next_program',
None,
"Toon Program",
'mdi:calendar-clock',
None,
),
]
if toon.thermostat_info.have_ot_boiler:
sensors.extend([
ToonBoilerBinarySensor(toon, 'thermostat_info',
'ot_communication_error', '0',
"OpenTherm Connection",
'mdi:check-network-outline',
'connectivity'),
ToonBoilerBinarySensor(toon, 'thermostat_info', 'error_found', 255,
"Boiler Status", 'mdi:alert', 'problem',
inverted=True),
ToonBoilerBinarySensor(toon, 'thermostat_info', 'burner_info',
None, "Boiler Burner", 'mdi:fire', None),
ToonBoilerBinarySensor(toon, 'thermostat_info', 'burner_info', '2',
"Hot Tap Water", 'mdi:water-pump', None),
ToonBoilerBinarySensor(toon, 'thermostat_info', 'burner_info', '3',
"Boiler Preheating", 'mdi:fire', None),
])
sensors.extend(
[
ToonBoilerBinarySensor(
toon,
'thermostat_info',
'ot_communication_error',
'0',
"OpenTherm Connection",
'mdi:check-network-outline',
'connectivity',
),
ToonBoilerBinarySensor(
toon,
'thermostat_info',
'error_found',
255,
"Boiler Status",
'mdi:alert',
'problem',
inverted=True,
),
ToonBoilerBinarySensor(
toon,
'thermostat_info',
'burner_info',
None,
"Boiler Burner",
'mdi:fire',
None,
),
ToonBoilerBinarySensor(
toon,
'thermostat_info',
'burner_info',
'2',
"Hot Tap Water",
'mdi:water-pump',
None,
),
ToonBoilerBinarySensor(
toon,
'thermostat_info',
'burner_info',
'3',
"Boiler Preheating",
'mdi:fire',
None,
),
]
)
async_add_entities(sensors)
async_add_entities(sensors, True)
class ToonBinarySensor(ToonEntity, BinarySensorDevice):
"""Defines an Toon binary sensor."""
def __init__(self, toon, section: str, measurement: str, on_value: Any,
name: str, icon: str, device_class: str,
inverted: bool = False) -> None:
def __init__(
self,
toon: ToonData,
section: str,
measurement: str,
on_value: Any,
name: str,
icon: str,
device_class: str,
inverted: bool = False,
) -> None:
"""Initialize the Toon sensor."""
self._state = inverted
self._device_class = device_class
@ -77,8 +137,16 @@ class ToonBinarySensor(ToonEntity, BinarySensorDevice):
@property
def unique_id(self) -> str:
"""Return the unique ID for this binary sensor."""
return '_'.join([DOMAIN, self.toon.agreement.id, 'binary_sensor',
self.section, self.measurement, str(self.on_value)])
return '_'.join(
[
DOMAIN,
self.toon.agreement.id,
'binary_sensor',
self.section,
self.measurement,
str(self.on_value),
]
)
@property
def device_class(self) -> str:
@ -118,8 +186,9 @@ class ToonDisplayBinarySensor(ToonBinarySensor, ToonDisplayDeviceEntity):
pass
class ToonBoilerModuleBinarySensor(ToonBinarySensor,
ToonBoilerModuleDeviceEntity):
class ToonBoilerModuleBinarySensor(
ToonBinarySensor, ToonBoilerModuleDeviceEntity
):
"""Defines a Boiler module binary sensor."""
pass

View File

@ -1,6 +1,5 @@
"""Support for Toon thermostat."""
from datetime import timedelta
import logging
from typing import Any, Dict, List, Optional
@ -12,39 +11,45 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.helpers.typing import HomeAssistantType
from . import ToonDisplayDeviceEntity
from .const import DATA_TOON_CLIENT, DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, DOMAIN
from . import ToonData, ToonDisplayDeviceEntity
from .const import (
DATA_TOON_CLIENT,
DATA_TOON,
DEFAULT_MAX_TEMP,
DEFAULT_MIN_TEMP,
DOMAIN,
)
_LOGGER = logging.getLogger(__name__)
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
SUPPORT_PRESET = [PRESET_AWAY, PRESET_COMFORT, PRESET_HOME, PRESET_SLEEP]
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
SCAN_INTERVAL = timedelta(seconds=300)
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry,
async_add_entities) -> None:
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
) -> None:
"""Set up a Toon binary sensors based on a config entry."""
toon = hass.data[DATA_TOON_CLIENT][entry.entry_id]
async_add_entities([ToonThermostatDevice(toon)], True)
toon_client = hass.data[DATA_TOON_CLIENT][entry.entry_id]
toon_data = hass.data[DATA_TOON][entry.entry_id]
async_add_entities([ToonThermostatDevice(toon_client, toon_data)], True)
class ToonThermostatDevice(ToonDisplayDeviceEntity, ClimateDevice):
"""Representation of a Toon climate device."""
def __init__(self, toon) -> None:
def __init__(self, toon_client, toon_data: ToonData) -> None:
"""Initialize the Toon climate device."""
self._state = None
self._client = toon_client
self._state = None
self._current_temperature = None
self._target_temperature = None
self._next_target_temperature = None
self._heating_type = None
super().__init__(toon, "Toon Thermostat", 'mdi:thermostat')
super().__init__(toon_data, "Toon Thermostat", 'mdi:thermostat')
@property
def unique_id(self) -> str:
@ -112,19 +117,19 @@ class ToonThermostatDevice(ToonDisplayDeviceEntity, ClimateDevice):
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the current state of the burner."""
return {
'heating_type': self._heating_type,
}
return {'heating_type': self._heating_type}
def set_temperature(self, **kwargs) -> None:
"""Change the setpoint of the thermostat."""
temperature = kwargs.get(ATTR_TEMPERATURE)
self.toon.thermostat = temperature
self._client.thermostat = temperature
self.schedule_update_ha_state()
def set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
if preset_mode is not None:
self.toon.thermostat_state = preset_mode
self._client.thermostat_state = preset_mode
self.schedule_update_ha_state()
def set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode."""

View File

@ -1,23 +1,23 @@
"""Constants for the Toon integration."""
from homeassistant.const import ENERGY_KILO_WATT_HOUR
from datetime import timedelta
DOMAIN = 'toon'
DATA_TOON = 'toon'
DATA_TOON_CONFIG = 'toon_config'
DATA_TOON_CLIENT = 'toon_client'
DATA_TOON_CONFIG = 'toon_config'
DATA_TOON_UPDATED = 'toon_updated'
CONF_CLIENT_ID = 'client_id'
CONF_CLIENT_SECRET = 'client_secret'
CONF_DISPLAY = 'display'
CONF_TENANT = 'tenant'
DEFAULT_SCAN_INTERVAL = timedelta(seconds=300)
DEFAULT_MAX_TEMP = 30.0
DEFAULT_MIN_TEMP = 6.0
CURRENCY_EUR = 'EUR'
POWER_WATT = 'W'
POWER_KWH = ENERGY_KILO_WATT_HOUR
RATIO_PERCENT = '%'
VOLUME_CM3 = 'CM3'
VOLUME_M3 = 'M3'

View File

@ -1,113 +1,232 @@
"""Support for Toon sensors."""
from datetime import timedelta
import logging
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT
from . import (ToonEntity, ToonElectricityMeterDeviceEntity,
ToonGasMeterDeviceEntity, ToonSolarDeviceEntity,
ToonBoilerDeviceEntity)
from .const import (CURRENCY_EUR, DATA_TOON_CLIENT, DOMAIN, POWER_KWH,
POWER_WATT, VOLUME_CM3, VOLUME_M3, RATIO_PERCENT)
from . import (
ToonData,
ToonEntity,
ToonElectricityMeterDeviceEntity,
ToonGasMeterDeviceEntity,
ToonSolarDeviceEntity,
ToonBoilerDeviceEntity,
)
from .const import (
CURRENCY_EUR,
DATA_TOON,
DOMAIN,
VOLUME_CM3,
VOLUME_M3,
RATIO_PERCENT,
)
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
SCAN_INTERVAL = timedelta(seconds=300)
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry,
async_add_entities) -> None:
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
) -> None:
"""Set up Toon sensors based on a config entry."""
toon = hass.data[DATA_TOON_CLIENT][entry.entry_id]
toon = hass.data[DATA_TOON][entry.entry_id]
sensors = [
ToonElectricityMeterDeviceSensor(toon, 'power', 'value',
"Current Power Usage",
'mdi:power-plug', POWER_WATT),
ToonElectricityMeterDeviceSensor(toon, 'power', 'average',
"Average Power Usage",
'mdi:power-plug', POWER_WATT),
ToonElectricityMeterDeviceSensor(toon, 'power', 'daily_value',
"Power Usage Today",
'mdi:power-plug', POWER_KWH),
ToonElectricityMeterDeviceSensor(toon, 'power', 'daily_cost',
"Power Cost Today",
'mdi:power-plug', CURRENCY_EUR),
ToonElectricityMeterDeviceSensor(toon, 'power', 'average_daily',
"Average Daily Power Usage",
'mdi:power-plug', POWER_KWH),
ToonElectricityMeterDeviceSensor(toon, 'power', 'meter_reading',
"Power Meter Feed IN Tariff 1",
'mdi:power-plug', POWER_KWH),
ToonElectricityMeterDeviceSensor(toon, 'power', 'meter_reading_low',
"Power Meter Feed IN Tariff 2",
'mdi:power-plug', POWER_KWH),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'value',
"Current Power Usage",
'mdi:power-plug',
POWER_WATT,
),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'average',
"Average Power Usage",
'mdi:power-plug',
POWER_WATT,
),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'daily_value',
"Power Usage Today",
'mdi:power-plug',
ENERGY_KILO_WATT_HOUR,
),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'daily_cost',
"Power Cost Today",
'mdi:power-plug',
CURRENCY_EUR,
),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'average_daily',
"Average Daily Power Usage",
'mdi:power-plug',
ENERGY_KILO_WATT_HOUR,
),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'meter_reading',
"Power Meter Feed IN Tariff 1",
'mdi:power-plug',
ENERGY_KILO_WATT_HOUR,
),
ToonElectricityMeterDeviceSensor(
toon,
'power',
'meter_reading_low',
"Power Meter Feed IN Tariff 2",
'mdi:power-plug',
ENERGY_KILO_WATT_HOUR,
),
]
if toon.gas:
sensors.extend([
ToonGasMeterDeviceSensor(toon, 'gas', 'value', "Current Gas Usage",
'mdi:gas-cylinder', VOLUME_CM3),
ToonGasMeterDeviceSensor(toon, 'gas', 'average',
"Average Gas Usage", 'mdi:gas-cylinder',
VOLUME_CM3),
ToonGasMeterDeviceSensor(toon, 'gas', 'daily_usage',
"Gas Usage Today", 'mdi:gas-cylinder',
VOLUME_M3),
ToonGasMeterDeviceSensor(toon, 'gas', 'average_daily',
"Average Daily Gas Usage",
'mdi:gas-cylinder', VOLUME_M3),
ToonGasMeterDeviceSensor(toon, 'gas', 'meter_reading', "Gas Meter",
'mdi:gas-cylinder', VOLUME_M3),
ToonGasMeterDeviceSensor(toon, 'gas', 'daily_cost',
"Gas Cost Today", 'mdi:gas-cylinder',
CURRENCY_EUR),
])
sensors.extend(
[
ToonGasMeterDeviceSensor(
toon,
'gas',
'value',
"Current Gas Usage",
'mdi:gas-cylinder',
VOLUME_CM3,
),
ToonGasMeterDeviceSensor(
toon,
'gas',
'average',
"Average Gas Usage",
'mdi:gas-cylinder',
VOLUME_CM3,
),
ToonGasMeterDeviceSensor(
toon,
'gas',
'daily_usage',
"Gas Usage Today",
'mdi:gas-cylinder',
VOLUME_M3,
),
ToonGasMeterDeviceSensor(
toon,
'gas',
'average_daily',
"Average Daily Gas Usage",
'mdi:gas-cylinder',
VOLUME_M3,
),
ToonGasMeterDeviceSensor(
toon,
'gas',
'meter_reading',
"Gas Meter",
'mdi:gas-cylinder',
VOLUME_M3,
),
ToonGasMeterDeviceSensor(
toon,
'gas',
'daily_cost',
"Gas Cost Today",
'mdi:gas-cylinder',
CURRENCY_EUR,
),
]
)
if toon.solar:
sensors.extend([
ToonSolarDeviceSensor(toon, 'solar', 'value',
"Current Solar Production",
'mdi:solar-power', POWER_WATT),
ToonSolarDeviceSensor(toon, 'solar', 'maximum',
"Max Solar Production", 'mdi:solar-power',
POWER_WATT),
ToonSolarDeviceSensor(toon, 'solar', 'produced',
"Solar Production to Grid",
'mdi:solar-power', POWER_WATT),
ToonSolarDeviceSensor(toon, 'solar', 'average_produced',
"Average Solar Production to Grid",
'mdi:solar-power', POWER_WATT),
ToonElectricityMeterDeviceSensor(toon, 'solar',
'meter_reading_produced',
"Power Meter Feed OUT Tariff 1",
'mdi:solar-power',
POWER_KWH),
ToonElectricityMeterDeviceSensor(toon, 'solar',
'meter_reading_low_produced',
"Power Meter Feed OUT Tariff 2",
'mdi:solar-power', POWER_KWH),
])
sensors.extend(
[
ToonSolarDeviceSensor(
toon,
'solar',
'value',
"Current Solar Production",
'mdi:solar-power',
POWER_WATT,
),
ToonSolarDeviceSensor(
toon,
'solar',
'maximum',
"Max Solar Production",
'mdi:solar-power',
POWER_WATT,
),
ToonSolarDeviceSensor(
toon,
'solar',
'produced',
"Solar Production to Grid",
'mdi:solar-power',
POWER_WATT,
),
ToonSolarDeviceSensor(
toon,
'solar',
'average_produced',
"Average Solar Production to Grid",
'mdi:solar-power',
POWER_WATT,
),
ToonElectricityMeterDeviceSensor(
toon,
'solar',
'meter_reading_produced',
"Power Meter Feed OUT Tariff 1",
'mdi:solar-power',
ENERGY_KILO_WATT_HOUR,
),
ToonElectricityMeterDeviceSensor(
toon,
'solar',
'meter_reading_low_produced',
"Power Meter Feed OUT Tariff 2",
'mdi:solar-power',
ENERGY_KILO_WATT_HOUR,
),
]
)
if toon.thermostat_info.have_ot_boiler:
sensors.extend([
ToonBoilerDeviceSensor(toon, 'thermostat_info',
'current_modulation_level',
"Boiler Modulation Level",
'mdi:percent',
RATIO_PERCENT),
])
sensors.extend(
[
ToonBoilerDeviceSensor(
toon,
'thermostat_info',
'current_modulation_level',
"Boiler Modulation Level",
'mdi:percent',
RATIO_PERCENT,
)
]
)
async_add_entities(sensors)
async_add_entities(sensors, True)
class ToonSensor(ToonEntity):
"""Defines a Toon sensor."""
def __init__(self, toon, section: str, measurement: str,
name: str, icon: str, unit_of_measurement: str) -> None:
def __init__(
self,
toon: ToonData,
section: str,
measurement: str,
name: str,
icon: str,
unit_of_measurement: str,
) -> None:
"""Initialize the Toon sensor."""
self._state = None
self._unit_of_measurement = unit_of_measurement
@ -119,8 +238,15 @@ class ToonSensor(ToonEntity):
@property
def unique_id(self) -> str:
"""Return the unique ID for this sensor."""
return '_'.join([DOMAIN, self.toon.agreement.id, 'sensor',
self.section, self.measurement])
return '_'.join(
[
DOMAIN,
self.toon.agreement.id,
'sensor',
self.section,
self.measurement,
]
)
@property
def state(self):
@ -137,34 +263,46 @@ class ToonSensor(ToonEntity):
section = getattr(self.toon, self.section)
value = None
if not section:
return
if self.section == 'power' and self.measurement == 'daily_value':
value = round((float(section.daily_usage)
+ float(section.daily_usage_low)) / 1000.0, 2)
value = round(
(float(section.daily_usage) + float(section.daily_usage_low))
/ 1000.0,
2,
)
if value is None:
value = getattr(section, self.measurement)
if self.section == 'power' and \
self.measurement in ['meter_reading', 'meter_reading_low',
'average_daily']:
value = round(float(value)/1000.0, 2)
if self.section == 'power' and self.measurement in [
'meter_reading',
'meter_reading_low',
'average_daily',
]:
value = round(float(value) / 1000.0, 2)
if self.section == 'solar' and \
self.measurement in ['meter_reading_produced',
'meter_reading_low_produced']:
value = float(value)/1000.0
if self.section == 'solar' and self.measurement in [
'meter_reading_produced',
'meter_reading_low_produced',
]:
value = float(value) / 1000.0
if self.section == 'gas' and \
self.measurement in ['average_daily', 'daily_usage',
'meter_reading']:
value = round(float(value)/1000.0, 2)
if self.section == 'gas' and self.measurement in [
'average_daily',
'daily_usage',
'meter_reading',
]:
value = round(float(value) / 1000.0, 2)
self._state = max(0, value)
class ToonElectricityMeterDeviceSensor(ToonSensor,
ToonElectricityMeterDeviceEntity):
"""Defines a Eletricity Meter sensor."""
class ToonElectricityMeterDeviceSensor(
ToonSensor, ToonElectricityMeterDeviceEntity
):
"""Defines a Electricity Meter sensor."""
pass

View File

@ -0,0 +1,6 @@
update:
description: Update all entities with fresh data from Toon
fields:
display:
description: Toon display to update (optional)
example: eneco-001-123456