Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert sense to use DataUpdateCoordinator for trends data #34160

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion homeassistant/components/sense/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import (
ACTIVE_UPDATE_RATE,
Expand All @@ -27,6 +28,7 @@
SENSE_DEVICES_DATA,
SENSE_DISCOVERED_DEVICES_DATA,
SENSE_TIMEOUT_EXCEPTIONS,
SENSE_TRENDS_COORDINATOR,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -111,9 +113,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
except SENSE_TIMEOUT_EXCEPTIONS:
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] = {
SENSE_DATA: gateway,
SENSE_DEVICES_DATA: sense_devices_data,
SENSE_TRENDS_COORDINATOR: trends_coordinator,
SENSE_DISCOVERED_DEVICES_DATA: sense_discovered_devices,
}

Expand All @@ -122,7 +138,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
hass.config_entries.async_forward_entry_setup(entry, component)
)

async def async_sense_update(now):
async def async_sense_update(_):
"""Retrieve latest state."""
try:
await gateway.update_realtime()
Expand Down
16 changes: 6 additions & 10 deletions homeassistant/components/sense/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ def __init__(self, sense_devices_data, device, sense_monitor_id):
self._unique_id = f"{sense_monitor_id}-{self._id}"
self._icon = sense_to_mdi(device["icon"])
self._sense_devices_data = sense_devices_data
self._undo_dispatch_subscription = None
self._state = None
self._available = False

Expand Down Expand Up @@ -123,17 +122,14 @@ def should_poll(self):

async def async_added_to_hass(self):
"""Register callbacks."""
self._undo_dispatch_subscription = async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
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
def _async_update_from_data(self):
"""Get the latest data, update state. Must not do I/O."""
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/sense/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
SENSE_DEVICE_UPDATE = "sense_devices_update"
SENSE_DEVICES_DATA = "sense_devices_data"
SENSE_DISCOVERED_DEVICES_DATA = "sense_discovered_devices"
SENSE_TRENDS_COORDINATOR = "sense_trends_coorindator"
bdraco marked this conversation as resolved.
Show resolved Hide resolved

ACTIVE_NAME = "Energy"
ACTIVE_TYPE = "active"
Expand Down
91 changes: 44 additions & 47 deletions homeassistant/components/sense/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Support for monitoring a Sense energy sensor."""
from datetime import timedelta
import logging

from homeassistant.const import (
Expand All @@ -11,7 +10,6 @@
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle

from .const import (
ACTIVE_NAME,
Expand All @@ -28,12 +26,9 @@
SENSE_DEVICE_UPDATE,
SENSE_DEVICES_DATA,
SENSE_DISCOVERED_DEVICES_DATA,
SENSE_TIMEOUT_EXCEPTIONS,
SENSE_TRENDS_COORDINATOR,
)

MIN_TIME_BETWEEN_DAILY_UPDATES = timedelta(seconds=300)


_LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -70,17 +65,18 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Sense sensor."""
data = hass.data[DOMAIN][config_entry.entry_id][SENSE_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)
async def update_trends():
"""Update the daily power usage."""
await data.update_trend_data()
# Request only in case it takes longer
# than 60s
await trends_coordinator.async_request_refresh()

sense_monitor_id = data.sense_monitor_id
sense_devices = hass.data[DOMAIN][config_entry.entry_id][
SENSE_DISCOVERED_DEVICES_DATA
]
await data.update_trend_data()

devices = [
SenseEnergyDevice(sense_devices_data, device, sense_monitor_id)
Expand Down Expand Up @@ -114,8 +110,7 @@ async def update_trends():
name,
sensor_type,
is_production,
update_trends,
var,
trends_coordinator,
unique_id,
)
)
Expand Down Expand Up @@ -146,7 +141,6 @@ def __init__(
self._sensor_type = sensor_type
self._is_production = is_production
self._state = None
self._undo_dispatch_subscription = None

@property
def name(self):
Expand Down Expand Up @@ -190,17 +184,14 @@ def should_poll(self):

async def async_added_to_hass(self):
"""Register callbacks."""
self._undo_dispatch_subscription = async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
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
def _async_update_from_data(self):
"""Update the sensor from the data. Must not do I/O."""
Expand All @@ -217,7 +208,7 @@ class SenseTrendsSensor(Entity):
"""Implementation of a Sense energy sensor."""

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."""
name_type = PRODUCTION_NAME if is_production else CONSUMPTION_NAME
Expand All @@ -226,10 +217,11 @@ def __init__(
self._available = False
self._data = data
self._sensor_type = sensor_type
self.update_sensor = update_call
self._coordinator = trends_coordinator
self._is_production = is_production
self._state = None
self._unit_of_measurement = ENERGY_KILO_WATT_HOUR
self._had_any_update = False

@property
def name(self):
Expand All @@ -239,12 +231,12 @@ def name(self):
@property
def state(self):
"""Return the state of the sensor."""
return self._state
return round(self._data.get_trend(self._sensor_type, self._is_production), 1)

@property
def available(self):
"""Return the availability of the sensor."""
return self._available
"""Return if entity is available."""
return self._had_any_update and self._coordinator.last_update_success

@property
def unit_of_measurement(self):
Expand All @@ -266,18 +258,27 @@ def unique_id(self):
"""Return the 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):
"""Get the latest data, update state."""
"""Update the entity.

try:
await self.update_sensor()
except SENSE_TIMEOUT_EXCEPTIONS:
_LOGGER.error("Timeout retrieving data")
return
Only used by the generic entity update service.
"""
await self._coordinator.async_request_refresh()

state = self._data.get_trend(self._sensor_type, self._is_production)
self._state = round(state, 1)
self._available = True
async def async_added_to_hass(self):
"""When entity is added to hass."""
self.async_on_remove(self._coordinator.async_add_listener(self._async_update))


class SenseEnergyDevice(Entity):
Expand All @@ -292,7 +293,6 @@ def __init__(self, sense_devices_data, device, sense_monitor_id):
self._unique_id = f"{sense_monitor_id}-{self._id}-{CONSUMPTION_ID}"
self._icon = sense_to_mdi(device["icon"])
self._sense_devices_data = sense_devices_data
self._undo_dispatch_subscription = None
self._state = None

@property
Expand Down Expand Up @@ -342,17 +342,14 @@ def should_poll(self):

async def async_added_to_hass(self):
"""Register callbacks."""
self._undo_dispatch_subscription = async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
self._async_update_from_data,
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{SENSE_DEVICE_UPDATE}-{self._sense_monitor_id}",
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
def _async_update_from_data(self):
"""Get the latest data, update state. Must not do I/O."""
Expand Down