Convert sense to use DataUpdateCoordinator for trends data (#34160)

* Convert sense to use DataUpdateCoordinator for trends

* remove unused

* request update right away

* clarify

* call async refresh later

* Update homeassistant/components/sense/__init__.py

Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>

* Update homeassistant/components/sense/__init__.py

Co-Authored-By: Paulus Schoutsen <paulus@home-assistant.io>

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
J. Nick Koston 2020-04-13 19:08:37 -05:00 committed by GitHub
parent e1d66f6fdd
commit 5ddcc22583
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 58 deletions

View file

@ -17,6 +17,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import ( from .const import (
ACTIVE_UPDATE_RATE, ACTIVE_UPDATE_RATE,
@ -27,6 +28,7 @@ from .const import (
SENSE_DEVICES_DATA, SENSE_DEVICES_DATA,
SENSE_DISCOVERED_DEVICES_DATA, SENSE_DISCOVERED_DEVICES_DATA,
SENSE_TIMEOUT_EXCEPTIONS, SENSE_TIMEOUT_EXCEPTIONS,
SENSE_TRENDS_COORDINATOR,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -111,9 +113,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
except SENSE_TIMEOUT_EXCEPTIONS: except SENSE_TIMEOUT_EXCEPTIONS:
raise ConfigEntryNotReady raise ConfigEntryNotReady
trends_coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"Sense Trends {email}",
update_method=gateway.update_trend_data,
update_interval=timedelta(seconds=300),
)
# This can take longer than 60s and we already know
# sense is online since get_discovered_device_data was
# successful so we do it later.
hass.loop.create_task(trends_coordinator.async_request_refresh())
hass.data[DOMAIN][entry.entry_id] = { hass.data[DOMAIN][entry.entry_id] = {
SENSE_DATA: gateway, SENSE_DATA: gateway,
SENSE_DEVICES_DATA: sense_devices_data, SENSE_DEVICES_DATA: sense_devices_data,
SENSE_TRENDS_COORDINATOR: trends_coordinator,
SENSE_DISCOVERED_DEVICES_DATA: sense_discovered_devices, SENSE_DISCOVERED_DEVICES_DATA: sense_discovered_devices,
} }
@ -122,7 +138,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.config_entries.async_forward_entry_setup(entry, component) hass.config_entries.async_forward_entry_setup(entry, component)
) )
async def async_sense_update(now): async def async_sense_update(_):
"""Retrieve latest state.""" """Retrieve latest state."""
try: try:
await gateway.update_realtime() await gateway.update_realtime()

View file

@ -72,7 +72,6 @@ class SenseDevice(BinarySensorDevice):
self._unique_id = f"{sense_monitor_id}-{self._id}" self._unique_id = f"{sense_monitor_id}-{self._id}"
self._icon = sense_to_mdi(device["icon"]) self._icon = sense_to_mdi(device["icon"])
self._sense_devices_data = sense_devices_data self._sense_devices_data = sense_devices_data
self._undo_dispatch_subscription = None
self._state = None self._state = None
self._available = False self._available = False
@ -123,16 +122,13 @@ class SenseDevice(BinarySensorDevice):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
self._undo_dispatch_subscription = async_dispatcher_connect( self.async_on_remove(
async_dispatcher_connect(
self.hass, self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}", f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data, self._async_update_from_data,
) )
)
async def async_will_remove_from_hass(self):
"""Undo subscription."""
if self._undo_dispatch_subscription:
self._undo_dispatch_subscription()
@callback @callback
def _async_update_from_data(self): def _async_update_from_data(self):

View file

@ -12,6 +12,7 @@ SENSE_DATA = "sense_data"
SENSE_DEVICE_UPDATE = "sense_devices_update" SENSE_DEVICE_UPDATE = "sense_devices_update"
SENSE_DEVICES_DATA = "sense_devices_data" SENSE_DEVICES_DATA = "sense_devices_data"
SENSE_DISCOVERED_DEVICES_DATA = "sense_discovered_devices" SENSE_DISCOVERED_DEVICES_DATA = "sense_discovered_devices"
SENSE_TRENDS_COORDINATOR = "sense_trends_coorindator"
ACTIVE_NAME = "Energy" ACTIVE_NAME = "Energy"
ACTIVE_TYPE = "active" ACTIVE_TYPE = "active"

View file

@ -1,5 +1,4 @@
"""Support for monitoring a Sense energy sensor.""" """Support for monitoring a Sense energy sensor."""
from datetime import timedelta
import logging import logging
from homeassistant.const import ( from homeassistant.const import (
@ -11,7 +10,6 @@ from homeassistant.const import (
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
from .const import ( from .const import (
ACTIVE_NAME, ACTIVE_NAME,
@ -28,12 +26,9 @@ from .const import (
SENSE_DEVICE_UPDATE, SENSE_DEVICE_UPDATE,
SENSE_DEVICES_DATA, SENSE_DEVICES_DATA,
SENSE_DISCOVERED_DEVICES_DATA, SENSE_DISCOVERED_DEVICES_DATA,
SENSE_TIMEOUT_EXCEPTIONS, SENSE_TRENDS_COORDINATOR,
) )
MIN_TIME_BETWEEN_DAILY_UPDATES = timedelta(seconds=300)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -70,17 +65,18 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Sense sensor.""" """Set up the Sense sensor."""
data = hass.data[DOMAIN][config_entry.entry_id][SENSE_DATA] data = hass.data[DOMAIN][config_entry.entry_id][SENSE_DATA]
sense_devices_data = hass.data[DOMAIN][config_entry.entry_id][SENSE_DEVICES_DATA] sense_devices_data = hass.data[DOMAIN][config_entry.entry_id][SENSE_DEVICES_DATA]
trends_coordinator = hass.data[DOMAIN][config_entry.entry_id][
SENSE_TRENDS_COORDINATOR
]
@Throttle(MIN_TIME_BETWEEN_DAILY_UPDATES) # Request only in case it takes longer
async def update_trends(): # than 60s
"""Update the daily power usage.""" await trends_coordinator.async_request_refresh()
await data.update_trend_data()
sense_monitor_id = data.sense_monitor_id sense_monitor_id = data.sense_monitor_id
sense_devices = hass.data[DOMAIN][config_entry.entry_id][ sense_devices = hass.data[DOMAIN][config_entry.entry_id][
SENSE_DISCOVERED_DEVICES_DATA SENSE_DISCOVERED_DEVICES_DATA
] ]
await data.update_trend_data()
devices = [ devices = [
SenseEnergyDevice(sense_devices_data, device, sense_monitor_id) SenseEnergyDevice(sense_devices_data, device, sense_monitor_id)
@ -114,8 +110,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
name, name,
sensor_type, sensor_type,
is_production, is_production,
update_trends, trends_coordinator,
var,
unique_id, unique_id,
) )
) )
@ -146,7 +141,6 @@ class SenseActiveSensor(Entity):
self._sensor_type = sensor_type self._sensor_type = sensor_type
self._is_production = is_production self._is_production = is_production
self._state = None self._state = None
self._undo_dispatch_subscription = None
@property @property
def name(self): def name(self):
@ -190,16 +184,13 @@ class SenseActiveSensor(Entity):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
self._undo_dispatch_subscription = async_dispatcher_connect( self.async_on_remove(
async_dispatcher_connect(
self.hass, self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}", f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data, self._async_update_from_data,
) )
)
async def async_will_remove_from_hass(self):
"""Undo subscription."""
if self._undo_dispatch_subscription:
self._undo_dispatch_subscription()
@callback @callback
def _async_update_from_data(self): def _async_update_from_data(self):
@ -217,7 +208,7 @@ class SenseTrendsSensor(Entity):
"""Implementation of a Sense energy sensor.""" """Implementation of a Sense energy sensor."""
def __init__( def __init__(
self, data, name, sensor_type, is_production, update_call, sensor_id, unique_id self, data, name, sensor_type, is_production, trends_coordinator, unique_id,
): ):
"""Initialize the Sense sensor.""" """Initialize the Sense sensor."""
name_type = PRODUCTION_NAME if is_production else CONSUMPTION_NAME name_type = PRODUCTION_NAME if is_production else CONSUMPTION_NAME
@ -226,10 +217,11 @@ class SenseTrendsSensor(Entity):
self._available = False self._available = False
self._data = data self._data = data
self._sensor_type = sensor_type self._sensor_type = sensor_type
self.update_sensor = update_call self._coordinator = trends_coordinator
self._is_production = is_production self._is_production = is_production
self._state = None self._state = None
self._unit_of_measurement = ENERGY_KILO_WATT_HOUR self._unit_of_measurement = ENERGY_KILO_WATT_HOUR
self._had_any_update = False
@property @property
def name(self): def name(self):
@ -239,12 +231,12 @@ class SenseTrendsSensor(Entity):
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the sensor."""
return self._state return round(self._data.get_trend(self._sensor_type, self._is_production), 1)
@property @property
def available(self): def available(self):
"""Return the availability of the sensor.""" """Return if entity is available."""
return self._available return self._had_any_update and self._coordinator.last_update_success
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):
@ -266,18 +258,27 @@ class SenseTrendsSensor(Entity):
"""Return the unique id.""" """Return the unique id."""
return self._unique_id return self._unique_id
@property
def should_poll(self):
"""No need to poll. Coordinator notifies entity of updates."""
return False
@callback
def _async_update(self):
"""Track if we had an update so we do not report zero data."""
self._had_any_update = True
self.async_write_ha_state()
async def async_update(self): async def async_update(self):
"""Get the latest data, update state.""" """Update the entity.
try: Only used by the generic entity update service.
await self.update_sensor() """
except SENSE_TIMEOUT_EXCEPTIONS: await self._coordinator.async_request_refresh()
_LOGGER.error("Timeout retrieving data")
return
state = self._data.get_trend(self._sensor_type, self._is_production) async def async_added_to_hass(self):
self._state = round(state, 1) """When entity is added to hass."""
self._available = True self.async_on_remove(self._coordinator.async_add_listener(self._async_update))
class SenseEnergyDevice(Entity): class SenseEnergyDevice(Entity):
@ -292,7 +293,6 @@ class SenseEnergyDevice(Entity):
self._unique_id = f"{sense_monitor_id}-{self._id}-{CONSUMPTION_ID}" self._unique_id = f"{sense_monitor_id}-{self._id}-{CONSUMPTION_ID}"
self._icon = sense_to_mdi(device["icon"]) self._icon = sense_to_mdi(device["icon"])
self._sense_devices_data = sense_devices_data self._sense_devices_data = sense_devices_data
self._undo_dispatch_subscription = None
self._state = None self._state = None
@property @property
@ -342,16 +342,13 @@ class SenseEnergyDevice(Entity):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Register callbacks.""" """Register callbacks."""
self._undo_dispatch_subscription = async_dispatcher_connect( self.async_on_remove(
async_dispatcher_connect(
self.hass, self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}", f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data, self._async_update_from_data,
) )
)
async def async_will_remove_from_hass(self):
"""Undo subscription."""
if self._undo_dispatch_subscription:
self._undo_dispatch_subscription()
@callback @callback
def _async_update_from_data(self): def _async_update_from_data(self):