I have created a custom component that reads the location from a gps device (it’s similar to the gaps integration but without the dependency on package gpsd
).
In async_setup
I create a task with hass.async_create_task
that periodically reads from the gps and updates state variables with the current location.
This work - EXCEPT homeassistant.bootstrap
takes forever to start and eventually times out.
How can I start the task without interfering with homeassistant.bootstrap
?
For completeness, here is the complete code of my component:
from __future__ import annotations
import asyncio
import logging
import pynmea2
import time
from homeassistant.core import HomeAssistant
from homeassistant.helpers.typing import ConfigType
from homeassistant.const import (
ATTR_LATITUDE,
ATTR_LONGITUDE,
CONF_ELEVATION
)
from serial.tools import list_ports
from serial_asyncio import open_serial_connection
_LOGGER = logging.getLogger(__name__)
DOMAIN = "gps"
BAUDRATE = 4800
def discover() -> str:
for port in list_ports.comports():
_LOGGER.info(f"Found device {port.vid:04x}:{port.pid:04x} by {port.manufacturer} @ {port.device}")
if port.vid == 0x067b and port.pid == 0x23a3 and "Prolific" in port.manufacturer:
return port.device
return None
async def read_gps(hass: HomeAssistant, reader):
# read & decode gps messages, send location to home assistant
low_quality = 0
while True:
line = await reader.readline()
line = line.decode()
try:
msg = pynmea2.parse(line)
if msg.gps_qual < 1:
low_quality += 1
if low_quality % 120 == 0:
_LOGGER.warning(f"{skip_count} low quality gps readings ignored")
continue
hass.states.async_set(f'{DOMAIN}.{ATTR_LATITUDE}', msg.latitude)
hass.states.async_set(f'{DOMAIN}.{ATTR_LONGITUDE}', msg.longitude)
hass.states.async_set(f'{DOMAIN}.{CONF_ELEVATION}', msg.altitude)
hass.states.async_set(f'{DOMAIN}.signal_quality', msg.gps_qual)
hass.states.async_set(f'{DOMAIN}.timestamp', time.ctime())
# update ~ once a minute
await asyncio.sleep(60)
except (AttributeError, pynmea2.ParseError) as e:
pass
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
# scan usb ports for gps device
print(f"print Search gps device ...")
_LOGGER.info(f"info Search gps device ...")
device = discover()
if device == None:
_LOGGER.error(f"No gps device found, stopping.")
return False
# start gps reader task
_LOGGER.info(f"Starting reader for gps at {device}")
reader, _ = await open_serial_connection(url=device, baudrate=BAUDRATE)
# PROBLEM: homeassistant.bootstrap won't finish startup
# hass.async_create_task(read_gps(hass, reader))
_LOGGER.info(f"Started gps reader as background task")
return True
And the manifest.json
:
{
"domain": "gps",
"name": "GPS reader",
"documentation": "https://github.com/iot49/HA-components.git/gps/",
"dependencies": [],
"codeowners": [],
"requirements": ["pyserial_asyncio", "pynmea2"],
"iot_class": "local_polling",
"version": "0.1.3"
}