Hello world!
So, I’m creating a custom integration for an ambient lighting project I’ve created called “Glimmr”.
Glimmr uses a webhook, and functions in a vaguely similar method to WLED, which is what I’ve based a lot of my code off of, including the python support library I created to interface with my API/Websocket, as well as the HA Integration itself.
Where I’m getting stuck is that my project uses SignalR for a websocket protocol, which means I’m using the signalrcore_async package for all of the heavy lifting.
SO, in the copied code I’m using from the WLED integration, I have the following bit of code in my coordinator for the websocket to handle creating the connection:
@callback
def _use_websocket(self) -> None:
"""Use WebSocket for updates, instead of polling."""
LOGGER.debug("Using websocket.")
async def listen() -> None:
"""Listen for state changes via WebSocket."""
LOGGER.debug("Listening")
try:
LOGGER.debug("Awaiting connection...")
await self.glimmr.connect()
LOGGER.debug("Connected!")
except GlimmrConnectionError as ce:
self.logger.info(ce)
except GlimmrError as err:
self.logger.info(err)
if self.unsub:
self.unsub()
self.unsub = None
return
The problem is the call to glimmr.connect(). When I fire that by itself, I get this error:
RuntimeError: I/O must be done in the executor; Use `await hass.async_add_executor_job()` at custom_components/glimmr/coordinator.py, line 75: await self.glimmr.connect()
My actual connect() method looks like this:
async def connect(self) -> None:
"""Connect to the WebSocket of a GLIMMR device.
Raises:
GLIMMRError: The configured GLIMMR device, does not support WebSocket
communications.
GLIMMRConnectionError: Error occurred while communicating with
the GLIMMR device via the WebSocket.
"""
if self.connected:
self.LOGGER.debug("Already connected.")
return
if not self.device:
self.LOGGER.debug("Updating device info?")
await self.update()
with Session():
url = "http://" + self.host + "/socket"
self.LOGGER.debug("Connecting to url: " + url)
connection = HubConnectionBuilder() \
.with_url(url) \
.build()
self.LOGGER.debug("Connected...")
if not self.session or not self.device:
self.LOGGER.debug("Unsupported session or device?")
raise GlimmrError(
"The Glimmr device at {self.host} does not support WebSockets"
)
try:
self.LOGGER.debug("Setting client.")
self._client = connection
self.LOGGER.debug("Starting connection.")
# start a connection
await connection.start()
self.LOGGER.debug("Connected!")
self._connected = True
connection.on('olo', self.olo)
self.LOGGER.debug("OLO!")
connection.on('mode', self.dev_mode)
self.LOGGER.debug("MODE!")
except (
) as exception:
self._connected = False
self.LOGGER.debug("Glimmr con exception...")
raise GlimmrConnectionError(
"Error occurred while communicating with GLIMMR device"
f" on WebSocket at {self.host}"
) from exception
If I try wrapping it in the await hass.async_add_executor_job()
method, it never executes, and the “Connected!” log line returns immediately.
So, trying to figure out the “right” way to tell HA to start up the websocket connection, and then add a callback whenever the data method fires from the socket, and then update my coordinator.
Full code for my integration and python libs are here:
And, for funsies, the actual repo is the same as the two above, minus the _ha or -python bits. I’m apparently limited to two links as a new user, but it’s not too tough to work out.