Stuck creating custom Integration with SignalR webhook

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. :smiley:

Update:

Got it all sorted. Wound up scrapping a lot of unnecessary code from the WLED-base, and wound up using a lot of code from the Wiz light custom integration.

It seems I had multiple issues going on - the biggest of which seems to be switching from signalrcore_async to regular signalrcore. Once I did that, the connect() method for my socket worked as expected.

I made my glimmr py library create a single class that holds all of the relevant data, and handles all of the update functions from the websocket. I then exposed the socket directly so I can just add my callbacks within HA to update state and connect the socket, and the rest just works.