mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Fix serial integration connection stability (#33067)
* Fix serial integration connection stability Retry to connect if serial device is not available on start, or connection is lost. Added some more log entries upon connection or when errors are encountered. * Updated code based on review * Fixed exception indentation * Handle SerialException * Sort imports * Update homeassistant/components/serial/sensor.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/serial/sensor.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * More clean up * Format and delint * Extract error handler * Clean up stop handler Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
d10f5a48d4
commit
e6a2507b50
@ -1,12 +1,15 @@
|
|||||||
"""Support for reading data from a serial port."""
|
"""Support for reading data from a serial port."""
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from serial import SerialException
|
||||||
import serial_asyncio
|
import serial_asyncio
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||||
from homeassistant.const import CONF_NAME, CONF_VALUE_TEMPLATE, EVENT_HOMEASSISTANT_STOP
|
from homeassistant.const import CONF_NAME, CONF_VALUE_TEMPLATE, EVENT_HOMEASSISTANT_STOP
|
||||||
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
@ -40,7 +43,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
|||||||
|
|
||||||
sensor = SerialSensor(name, port, baudrate, value_template)
|
sensor = SerialSensor(name, port, baudrate, value_template)
|
||||||
|
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, sensor.stop_serial_read())
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, sensor.stop_serial_read)
|
||||||
async_add_entities([sensor], True)
|
async_add_entities([sensor], True)
|
||||||
|
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ class SerialSensor(Entity):
|
|||||||
self._baudrate = baudrate
|
self._baudrate = baudrate
|
||||||
self._serial_loop_task = None
|
self._serial_loop_task = None
|
||||||
self._template = value_template
|
self._template = value_template
|
||||||
self._attributes = []
|
self._attributes = None
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Handle when an entity is about to be added to Home Assistant."""
|
"""Handle when an entity is about to be added to Home Assistant."""
|
||||||
@ -65,28 +68,61 @@ class SerialSensor(Entity):
|
|||||||
|
|
||||||
async def serial_read(self, device, rate, **kwargs):
|
async def serial_read(self, device, rate, **kwargs):
|
||||||
"""Read the data from the port."""
|
"""Read the data from the port."""
|
||||||
reader, _ = await serial_asyncio.open_serial_connection(
|
logged_error = False
|
||||||
url=device, baudrate=rate, **kwargs
|
|
||||||
)
|
|
||||||
while True:
|
while True:
|
||||||
line = await reader.readline()
|
|
||||||
line = line.decode("utf-8").strip()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(line)
|
reader, _ = await serial_asyncio.open_serial_connection(
|
||||||
if isinstance(data, dict):
|
url=device, baudrate=rate, **kwargs
|
||||||
self._attributes = data
|
)
|
||||||
except ValueError:
|
except SerialException as exc:
|
||||||
pass
|
if not logged_error:
|
||||||
|
_LOGGER.exception(
|
||||||
|
"Unable to connect to the serial device %s: %s. Will retry",
|
||||||
|
device,
|
||||||
|
exc,
|
||||||
|
)
|
||||||
|
logged_error = True
|
||||||
|
await self._handle_error()
|
||||||
|
else:
|
||||||
|
_LOGGER.info("Serial device %s connected", device)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = await reader.readline()
|
||||||
|
except SerialException as exc:
|
||||||
|
_LOGGER.exception(
|
||||||
|
"Error while reading serial device %s: %s", device, exc
|
||||||
|
)
|
||||||
|
await self._handle_error()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
line = line.decode("utf-8").strip()
|
||||||
|
|
||||||
if self._template is not None:
|
try:
|
||||||
line = self._template.async_render_with_possible_json_value(line)
|
data = json.loads(line)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if isinstance(data, dict):
|
||||||
|
self._attributes = data
|
||||||
|
|
||||||
_LOGGER.debug("Received: %s", line)
|
if self._template is not None:
|
||||||
self._state = line
|
line = self._template.async_render_with_possible_json_value(
|
||||||
self.async_write_ha_state()
|
line
|
||||||
|
)
|
||||||
|
|
||||||
async def stop_serial_read(self):
|
_LOGGER.debug("Received: %s", line)
|
||||||
|
self._state = line
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def _handle_error(self):
|
||||||
|
"""Handle error for serial connection."""
|
||||||
|
self._state = None
|
||||||
|
self._attributes = None
|
||||||
|
self.async_write_ha_state()
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def stop_serial_read(self, event):
|
||||||
"""Close resources."""
|
"""Close resources."""
|
||||||
if self._serial_loop_task:
|
if self._serial_loop_task:
|
||||||
self._serial_loop_task.cancel()
|
self._serial_loop_task.cancel()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user