mirror of
https://github.com/home-assistant/core.git
synced 2026-09-13 04:01:03 +01:00
81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
"""Live event stream for the Peblar integration."""
|
|
|
|
import asyncio
|
|
|
|
from peblar import Peblar, PeblarError, PeblarSessionStatus
|
|
|
|
from homeassistant.core import HomeAssistant, callback
|
|
|
|
from .const import EVENT_STREAM_RETRY_MAXIMUM, EVENT_STREAM_RETRY_MINIMUM, LOGGER
|
|
from .coordinator import PeblarConfigEntry, PeblarDataUpdateCoordinator
|
|
|
|
|
|
class PeblarSessionListener:
|
|
"""Follows the charging session over the charger's event stream.
|
|
|
|
The charger pushes a session change as it happens, which the poll
|
|
would otherwise take up to its interval to notice. This only tells the
|
|
poll to catch up early, so a stream that never comes up, or one that
|
|
falls over, costs nothing beyond going back to the poll on its own.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
hass: HomeAssistant,
|
|
entry: PeblarConfigEntry,
|
|
peblar: Peblar,
|
|
coordinator: PeblarDataUpdateCoordinator,
|
|
) -> None:
|
|
"""Initialize the listener."""
|
|
self._hass = hass
|
|
self._entry = entry
|
|
self._peblar = peblar
|
|
self._coordinator = coordinator
|
|
self._retry = EVENT_STREAM_RETRY_MINIMUM
|
|
|
|
async def async_run(self) -> None:
|
|
"""Keep a subscription up for as long as the entry is loaded."""
|
|
self._retry = EVENT_STREAM_RETRY_MINIMUM
|
|
|
|
while True:
|
|
try:
|
|
await self._async_listen()
|
|
except PeblarError as error:
|
|
LOGGER.debug(
|
|
"Peblar event stream for %s stopped: %s", self._entry.title, error
|
|
)
|
|
|
|
await asyncio.sleep(self._retry.total_seconds())
|
|
self._retry = min(self._retry * 2, EVENT_STREAM_RETRY_MAXIMUM)
|
|
|
|
async def _async_listen(self) -> None:
|
|
"""Open the stream and stay on it until it closes."""
|
|
websocket = self._peblar.websocket()
|
|
try:
|
|
await websocket.connect()
|
|
await websocket.subscribe_session_status(self._handle_session_status)
|
|
|
|
# A charger that was unreachable at startup can leave the wait
|
|
# at its longest. A subscription that landed settles that, so a
|
|
# drop hours later is not held against whatever went before.
|
|
# Taking the socket without ever getting this far is not a
|
|
# working stream, and keeps backing off.
|
|
self._retry = EVENT_STREAM_RETRY_MINIMUM
|
|
|
|
await websocket.listen()
|
|
finally:
|
|
await websocket.disconnect()
|
|
|
|
@callback
|
|
def _handle_session_status(self, status: PeblarSessionStatus) -> None:
|
|
"""Ask the poll to catch up, now the session has moved on.
|
|
|
|
The charger sends the current status right after subscribing, so
|
|
the first call says nothing new. Refreshing anyway is harmless and
|
|
cheaper than working out which one that was.
|
|
"""
|
|
LOGGER.debug("Peblar session for %s is %s", self._entry.title, status.state)
|
|
self._entry.async_create_task(
|
|
self._hass, self._coordinator.async_request_refresh(), eager_start=False
|
|
)
|