Skip to content

Commit 5889dc3

Browse files
authored
Implement heartbeat handling (#73)
1 parent 8fc1ccd commit 5889dc3

File tree

1 file changed

+31
-0
lines changed

1 file changed

+31
-0
lines changed

custom_components/remote_homeassistant/__init__.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@
104104
}),
105105
}, extra=vol.ALLOW_EXTRA)
106106

107+
HEARTBEAT_INTERVAL = 20
108+
HEARTBEAT_TIMEOUT = 5
109+
107110

108111
def async_yaml_to_config_entry(instance_conf):
109112
"""Convert YAML config into data and options used by a config entry."""
@@ -268,6 +271,7 @@ def __init__(self, hass, config_entry):
268271
self._connection_state_entity = '{}remote_connection_{}_{}'.format(self._connection_state_entity, self._entry.data[CONF_HOST].replace('.', '_').replace('-', '_'), self._entry.data[CONF_PORT])
269272

270273
self._connection = None
274+
self._heartbeat_task = None
271275
self._is_stopping = False
272276
self._entities = set()
273277
self._all_entity_names = set()
@@ -360,6 +364,29 @@ async def _async_instance_id_match():
360364
self._hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _async_stop_handler)
361365

362366
asyncio.ensure_future(self._recv())
367+
self._heartbeat_task = self._hass.loop.create_task(self._heartbeat_loop())
368+
369+
async def _heartbeat_loop(self):
370+
"""Send periodic heartbeats to remote instance."""
371+
while not self._connection.closed:
372+
await asyncio.sleep(HEARTBEAT_INTERVAL)
373+
374+
_LOGGER.debug("Sending ping")
375+
event = asyncio.Event()
376+
def resp(message):
377+
_LOGGER.debug("Got pong: %s", message)
378+
event.set()
379+
380+
await self._call(resp, "ping")
381+
382+
try:
383+
await asyncio.wait_for(event.wait(), HEARTBEAT_TIMEOUT)
384+
except asyncio.TimeoutError:
385+
_LOGGER.error("heartbeat failed")
386+
387+
# Schedule closing on event loop to avoid deadlock
388+
asyncio.ensure_future(self._connection.close())
389+
break
363390

364391
async def async_stop(self):
365392
"""Close connection."""
@@ -387,10 +414,14 @@ async def _disconnected(self):
387414
# Remove all published entries
388415
for entity in self._entities:
389416
self._hass.states.async_remove(entity)
417+
if self._heartbeat_task is not None:
418+
self._heartbeat_task.cancel()
419+
await self._heartbeat_task
390420
if self._remove_listener is not None:
391421
self._remove_listener()
392422

393423
self.set_connection_state(STATE_DISCONNECTED)
424+
self._heartbeat_task = None
394425
self._remove_listener = None
395426
self._entities = set()
396427
self._all_entity_names = set()

0 commit comments

Comments
 (0)