Reconnecting client: optional keep-alive & timeout

- lirc timeout, to reliably detect disconnect
This commit is contained in:
Martin Bauer
2020-07-13 11:32:38 +00:00
parent feeefbe987
commit 349861ad7c
3 changed files with 66 additions and 31 deletions

View File

@@ -2,6 +2,7 @@ import asyncio
import logging
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.components.binary_sensor import BinarySensorDevice
import socket
class IsConnectedSensor(BinarySensorDevice):
@@ -12,16 +13,16 @@ class IsConnectedSensor(BinarySensorDevice):
async def set_value(self, value):
self._on = value
await self.async_update_ha_state()
# this doesn't work sometimes - but polling is enabled anyway
try:
await asyncio.wait_for(self.async_update_ha_state(), 1)
except Exception:
pass
@property
def name(self):
return self._name
@property
def should_poll(self):
return False
@property
def available(self) -> bool:
return True
@@ -30,13 +31,20 @@ class IsConnectedSensor(BinarySensorDevice):
def is_on(self):
return self._on
def refresh(self):
pass
def update(self):
pass
_LOGGER = logging.getLogger(__name__)
class ReconnectingClient:
def __init__(self, hass, host, port, connection_name, receive_line_callback, connection_status_changed_callback):
def __init__(self, hass, host, port, connection_name, receive_line_callback,
connection_status_changed_callback, timeout=None):
self.connected = False
self.reconnect_time_start = 1
self.reconnect_time_max = 60
@@ -52,6 +60,7 @@ class ReconnectingClient:
self._connection_name = connection_name
self._connection_task = None
self._connected_sensor = None
self._timeout = timeout
@property
def connected_sensor(self):
@@ -100,23 +109,25 @@ class ReconnectingClient:
self.reconnect_time = self.reconnect_time_start
while self._run:
line = await reader.readline()
if self._timeout:
line = await asyncio.wait_for(reader.readline(), self._timeout)
else:
line = await reader.readline()
if not line:
raise OSError("Disconnect")
line = line.decode()
_LOGGER.debug(f"{self._connection_name} received line - passing along '{line}'")
await self._receive_line_callback(line)
except OSError as e:
except (OSError, asyncio.TimeoutError):
if self._connection_last_state != 'FAILED':
notification_text = "{} connection to {}:{} failed".format(self._connection_name, self._host,
self._port)
notification_text = f"{self._connection_name} connection to {self._host}:{self._port} failed"
self.hass.components.persistent_notification.async_create(notification_text, title="No connection")
_LOGGER.error("Connection to {} failed {}:{}".format(self._connection_name, self._host, self._port))
_LOGGER.error(f"Connection to {self._connection_name} failed {self._host}:{self._port}")
await self._connection_status_changed_callback('disconnected')
if self._connected_sensor:
await self._connected_sensor.set_value(False)
_LOGGER.error(
"After connection failed ({} {}:{})".format(self._connection_name, self._host, self._port))
await asyncio.wait_for(self._connected_sensor.set_value(False), timeout=2.0)
_LOGGER.error(f"After setting sensor ({self._connection_name} {self._host}:{self._port})")
_LOGGER.error(f"After connection failed ({self._connection_name} {self._host}:{self._port})")
else:
_LOGGER.debug(f"{self._connection_name} retried connection, last state {self._connection_last_state}"
f"reconnection time {self.reconnect_time}")