core/homeassistant/components/sense/__init__.py

179 lines
5.9 KiB
Python
Raw Normal View History

"""Support for monitoring a Sense energy sensor."""
from datetime import timedelta
2019-12-01 05:35:45 +00:00
import logging
from sense_energy import (
ASyncSenseable,
SenseAuthenticationException,
SenseMFARequiredException,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_EMAIL,
CONF_TIMEOUT,
EVENT_HOMEASSISTANT_STOP,
2021-12-04 12:43:48 +00:00
Platform,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
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,
DOMAIN,
Fix handling of dns resolution failure during sense setup (#88279) ``` 2023-02-16 20:44:54.516 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry alexander for sense Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 861, in _resolve_host await event.wait() File "/usr/local/lib/python3.10/site-packages/aiohttp/locks.py", line 34, in wait raise self._exc File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 874, in _resolve_host addrs = await self._resolver.resolve(host, port, family=self._family) File "/usr/local/lib/python3.10/site-packages/aiohttp/resolver.py", line 33, in resolve infos = await self._loop.getaddrinfo( File "/usr/local/lib/python3.10/asyncio/base_events.py", line 860, in getaddrinfo return await self.run_in_executor( File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno -3] Try again The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 381, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/sense/__init__.py", line 83, in async_setup_entry await gateway.get_monitor_data() File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 214, in get_monitor_data json = await self._api_call("app/monitors/%s/overview" % self.sense_monitor_id) File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 174, in _api_call async with self._client_session.get( File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 1141, in __aenter__ self._resp = await self._coro File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 536, in _request conn = await self._connector.connect( File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 540, in connect proto = await self._create_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 901, in _create_connection _, proto = await self._create_direct_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1166, in _create_direct_connection raise ClientConnectorError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.sense.com:443 ssl:default [Try again] ```
2023-02-17 04:18:16 +00:00
SENSE_CONNECT_EXCEPTIONS,
SENSE_DATA,
SENSE_DEVICE_UPDATE,
SENSE_DEVICES_DATA,
SENSE_DISCOVERED_DEVICES_DATA,
SENSE_TIMEOUT_EXCEPTIONS,
SENSE_TRENDS_COORDINATOR,
Fix handling of dns resolution failure during sense setup (#88279) ``` 2023-02-16 20:44:54.516 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry alexander for sense Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 861, in _resolve_host await event.wait() File "/usr/local/lib/python3.10/site-packages/aiohttp/locks.py", line 34, in wait raise self._exc File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 874, in _resolve_host addrs = await self._resolver.resolve(host, port, family=self._family) File "/usr/local/lib/python3.10/site-packages/aiohttp/resolver.py", line 33, in resolve infos = await self._loop.getaddrinfo( File "/usr/local/lib/python3.10/asyncio/base_events.py", line 860, in getaddrinfo return await self.run_in_executor( File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno -3] Try again The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 381, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/sense/__init__.py", line 83, in async_setup_entry await gateway.get_monitor_data() File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 214, in get_monitor_data json = await self._api_call("app/monitors/%s/overview" % self.sense_monitor_id) File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 174, in _api_call async with self._client_session.get( File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 1141, in __aenter__ self._resp = await self._coro File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 536, in _request conn = await self._connector.connect( File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 540, in connect proto = await self._create_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 901, in _create_connection _, proto = await self._create_direct_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1166, in _create_direct_connection raise ClientConnectorError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.sense.com:443 ssl:default [Try again] ```
2023-02-17 04:18:16 +00:00
SENSE_WEBSOCKET_EXCEPTIONS,
)
_LOGGER = logging.getLogger(__name__)
2021-12-04 12:43:48 +00:00
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
class SenseDevicesData:
"""Data for each sense device."""
def __init__(self):
"""Create."""
self._data_by_device = {}
def set_devices_data(self, devices):
"""Store a device update."""
self._data_by_device = {device["id"]: device for device in devices}
def get_device_by_id(self, sense_device_id):
"""Get the latest device data."""
return self._data_by_device.get(sense_device_id)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Sense from a config entry."""
entry_data = entry.data
email = entry_data[CONF_EMAIL]
timeout = entry_data[CONF_TIMEOUT]
access_token = entry_data.get("access_token", "")
user_id = entry_data.get("user_id", "")
device_id = entry_data.get("device_id", "")
refresh_token = entry_data.get("refresh_token", "")
monitor_id = entry_data.get("monitor_id", "")
client_session = async_get_clientsession(hass)
gateway = ASyncSenseable(
api_timeout=timeout, wss_timeout=timeout, client_session=client_session
)
gateway.rate_limit = ACTIVE_UPDATE_RATE
try:
gateway.load_auth(access_token, user_id, device_id, refresh_token)
gateway.set_monitor_id(monitor_id)
await gateway.get_monitor_data()
except (SenseAuthenticationException, SenseMFARequiredException) as err:
_LOGGER.warning("Sense authentication expired")
raise ConfigEntryAuthFailed(err) from err
except SENSE_TIMEOUT_EXCEPTIONS as err:
raise ConfigEntryNotReady(
str(err) or "Timed out during authentication"
) from err
Fix handling of dns resolution failure during sense setup (#88279) ``` 2023-02-16 20:44:54.516 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry alexander for sense Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 861, in _resolve_host await event.wait() File "/usr/local/lib/python3.10/site-packages/aiohttp/locks.py", line 34, in wait raise self._exc File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 874, in _resolve_host addrs = await self._resolver.resolve(host, port, family=self._family) File "/usr/local/lib/python3.10/site-packages/aiohttp/resolver.py", line 33, in resolve infos = await self._loop.getaddrinfo( File "/usr/local/lib/python3.10/asyncio/base_events.py", line 860, in getaddrinfo return await self.run_in_executor( File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno -3] Try again The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 381, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/sense/__init__.py", line 83, in async_setup_entry await gateway.get_monitor_data() File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 214, in get_monitor_data json = await self._api_call("app/monitors/%s/overview" % self.sense_monitor_id) File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 174, in _api_call async with self._client_session.get( File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 1141, in __aenter__ self._resp = await self._coro File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 536, in _request conn = await self._connector.connect( File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 540, in connect proto = await self._create_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 901, in _create_connection _, proto = await self._create_direct_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1166, in _create_direct_connection raise ClientConnectorError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.sense.com:443 ssl:default [Try again] ```
2023-02-17 04:18:16 +00:00
except SENSE_CONNECT_EXCEPTIONS as err:
raise ConfigEntryNotReady(str(err)) from err
sense_devices_data = SenseDevicesData()
try:
sense_discovered_devices = await gateway.get_discovered_device_data()
await gateway.update_realtime()
except SENSE_TIMEOUT_EXCEPTIONS as err:
raise ConfigEntryNotReady(
str(err) or "Timed out during realtime update"
) from err
Fix handling of dns resolution failure during sense setup (#88279) ``` 2023-02-16 20:44:54.516 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry alexander for sense Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 861, in _resolve_host await event.wait() File "/usr/local/lib/python3.10/site-packages/aiohttp/locks.py", line 34, in wait raise self._exc File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 874, in _resolve_host addrs = await self._resolver.resolve(host, port, family=self._family) File "/usr/local/lib/python3.10/site-packages/aiohttp/resolver.py", line 33, in resolve infos = await self._loop.getaddrinfo( File "/usr/local/lib/python3.10/asyncio/base_events.py", line 860, in getaddrinfo return await self.run_in_executor( File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno -3] Try again The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 381, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/sense/__init__.py", line 83, in async_setup_entry await gateway.get_monitor_data() File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 214, in get_monitor_data json = await self._api_call("app/monitors/%s/overview" % self.sense_monitor_id) File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 174, in _api_call async with self._client_session.get( File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 1141, in __aenter__ self._resp = await self._coro File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 536, in _request conn = await self._connector.connect( File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 540, in connect proto = await self._create_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 901, in _create_connection _, proto = await self._create_direct_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1166, in _create_direct_connection raise ClientConnectorError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.sense.com:443 ssl:default [Try again] ```
2023-02-17 04:18:16 +00:00
except SENSE_WEBSOCKET_EXCEPTIONS as err:
raise ConfigEntryNotReady(str(err) or "Error during realtime update") from err
async def _async_update_trend():
"""Update the trend data."""
try:
await gateway.update_trend_data()
except (SenseAuthenticationException, SenseMFARequiredException) as err:
_LOGGER.warning("Sense authentication expired")
raise ConfigEntryAuthFailed(err) from err
trends_coordinator: DataUpdateCoordinator[None] = DataUpdateCoordinator(
hass,
_LOGGER,
name=f"Sense Trends {email}",
update_method=_async_update_trend,
update_interval=timedelta(seconds=300),
)
# Start out as unavailable so we do not report 0 data
# until the update happens
trends_coordinator.last_update_success = False
# 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.
2023-02-17 19:54:34 +00:00
entry.async_create_background_task(
hass,
trends_coordinator.async_request_refresh(),
"sense.trends-coordinator-refresh",
)
hass.data.setdefault(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,
}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async def async_sense_update(_):
"""Retrieve latest state."""
try:
await gateway.update_realtime()
except SENSE_TIMEOUT_EXCEPTIONS as ex:
_LOGGER.error("Timeout retrieving data: %s", ex)
Fix handling of dns resolution failure during sense setup (#88279) ``` 2023-02-16 20:44:54.516 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry alexander for sense Traceback (most recent call last): File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1152, in _create_direct_connection hosts = await asyncio.shield(host_resolved) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 861, in _resolve_host await event.wait() File "/usr/local/lib/python3.10/site-packages/aiohttp/locks.py", line 34, in wait raise self._exc File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 874, in _resolve_host addrs = await self._resolver.resolve(host, port, family=self._family) File "/usr/local/lib/python3.10/site-packages/aiohttp/resolver.py", line 33, in resolve infos = await self._loop.getaddrinfo( File "/usr/local/lib/python3.10/asyncio/base_events.py", line 860, in getaddrinfo return await self.run_in_executor( File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): socket.gaierror: [Errno -3] Try again The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/config_entries.py", line 381, in async_setup result = await component.async_setup_entry(hass, self) File "/usr/src/homeassistant/homeassistant/components/sense/__init__.py", line 83, in async_setup_entry await gateway.get_monitor_data() File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 214, in get_monitor_data json = await self._api_call("app/monitors/%s/overview" % self.sense_monitor_id) File "/usr/local/lib/python3.10/site-packages/sense_energy/asyncsenseable.py", line 174, in _api_call async with self._client_session.get( File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 1141, in __aenter__ self._resp = await self._coro File "/usr/local/lib/python3.10/site-packages/aiohttp/client.py", line 536, in _request conn = await self._connector.connect( File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 540, in connect proto = await self._create_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 901, in _create_connection _, proto = await self._create_direct_connection(req, traces, timeout) File "/usr/local/lib/python3.10/site-packages/aiohttp/connector.py", line 1166, in _create_direct_connection raise ClientConnectorError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.sense.com:443 ssl:default [Try again] ```
2023-02-17 04:18:16 +00:00
except SENSE_WEBSOCKET_EXCEPTIONS as ex:
_LOGGER.error("Failed to update data: %s", ex)
data = gateway.get_realtime()
if "devices" in data:
sense_devices_data.set_devices_data(data["devices"])
async_dispatcher_send(hass, f"{SENSE_DEVICE_UPDATE}-{gateway.sense_monitor_id}")
remove_update_callback = async_track_time_interval(
2019-07-31 19:25:30 +00:00
hass, async_sense_update, timedelta(seconds=ACTIVE_UPDATE_RATE)
)
@callback
def _remove_update_callback_at_stop(event):
remove_update_callback()
entry.async_on_unload(remove_update_callback)
entry.async_on_unload(
hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, _remove_update_callback_at_stop
)
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok