Starting a websocket connection in async_setup_entry via hass.async_create_task causes long startup

Hi,

I am working on a new light integration that gets push updates via websocket. The way I have implemented this is by starting the websocket connection in async_setup_entry in light.py, creating it as a task on the event loop via the hass.async_create_task. This works well with regards to light functionality. The issue I am facing though is that it makes the HA starting time very long with HA saying “Wrapping up startup, not everything will be available until it is finished.” for 3-5 minutes. I assume it is waiting for this task and eventually gives up. Start up time is fast when I don’t call the websocket. HA doesn’t give any warnings about the integration loading time (i.e. saying it takes more than 10 seconds, etc.).

Is there a better way to start the websocket function without causing such a long starting time?

Thanks.

Post a link to your code to help people help you better.

I suspect async_create_task is awated and therefore blocking async_setup_entry returning until some timeout. It is often better to use call later with a small delay to stop this hapening.

Thank you Mark. I prefer not to share my code currently - I am not a programmer and my code is incredibly messy and goes against all rules and regulations (the shame!) (but it works).

I think you are right and I am happy to call it later but I don’t know how or from where. Any suggestions?

All I have in light.py are async_setup_entry, class MyCoordinator(DataUpdateCoordinator) and the entity class. So I am not sure how I call it once async_setup_entry is finished.

It is similar issue to this one:

However, in my case the connection needs to stay open all the time to wait messages so Mark’s solution in that other case won’t work.

The relevant parts of my code are:

async def async_setup_entry(
    hass: HomeAssistant,
    # config: ConfigType,
    config_entry: config_entries.ConfigEntry,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:

    ...

    hass.async_create_task(
        pixie_websocket_connect(
        applicationid,
        installationid,
        javascriptkey,
        devices_list[0]["sessiontoken"],
        devices_list[0]["userid"],
        devices_list[0]["homeid"],
        devices_list[0]["livegroup_objectid"],
        coordinator,
        hass,
        )
   )

async def pixie_websocket_connect(
    ApplicationId,
    InstallationID,
    JavaScriptKey,
    SessionToken,
    UserID,
    HomeID,
    LiveGroup_bjectID,
    coordinator,
    hass,
):

     ...

    while True:
            try:
                ws_update = await websocket.recv()
            except websockets.ConnectionClosed:
                _LOGGER.warning("websocket disconnected, reconnecting")
                break

            ws_update = json.loads(ws_update)
            # print(json.dumps(ws_update, indent=4))
            # _LOGGER.info(ws_update)
            if "op" in ws_update:
                if ws_update["op"] == "update":
                    if "deviceList" in ws_update["object"]:
                        devices_list = pixiepluslogin.parse_ws_data(
                            ws_update,
                            ApplicationId,
                            InstallationID,
                            JavaScriptKey,
                            SessionToken,
                            UserID,
                            HomeID,
                            LiveGroup_bjectID,
                        )
                        coordinator.async_set_updated_data(devices_list)
1 Like

Using asyncio.create_task instead of hass.async_create_task seems to work, based on:

You can add a listener to wait for home assistant to have started and use this to call your routine to start you websocket. The developer docs has a great example of how to do this here.

However, instead use homeassistant_start as the event to listen to.

Thank you, will give it a try.

I came across this thread when looking for a solution to this same issue, so just wanted to update it in case others are also using it as reference. The “correct” approach seems to be to use a listener as @msp1974 says, but there’s actually a help to make this more straightforwards. If you

from homeassistant.helpers.start import async_at_start

then you can call your function

async_at_start(self._hass,  self._async_startup)

and you function then has the tasks to run once the bootstrap has finished

async def _async_startup(self):
self._notifier_task = self._hass.async_create_task(self._device.run_notifier())

Anyway, just for reference as it wasn’t clear for me the best way to setup a listener…

… and one new fyi on this… There’s now a specific background task method

async def _async_startup(self, loop):

     self._notifier_task = self._hass.async_create_background_task(
            self._device.run_notifier(), name="notifier task"
    )
1 Like