Wait for EVENT_HOMEASSISTANT_STARTED

Hi!

I’m working with the PubSub integration. I’d like to avoid having repeated events everytime I restart hass, so instead of starting listening to the event bus straight away I’d like to wait for EVENT_HOMEASSISTANT_STARTED and, then, start actually listening on the bus and sending events to the queue.

What’s the right way to do this? Do we have any examples on other integrations?

Thanks!

Sure, lots of things do this. Anything that listens for a user-configured trigger does for sure but it’s all over. Here’s the snippet automations use for this:

You can find something similar all over in setup steps of many other integrations.

Thanks! I’ll play around with this. I understand this is an async lock, meaning that the integration setup won’t continue further until the signal is received, am I right?

EDIT: Nevermind, I’m reading the docs, and saw there’s a callback when the event is received.

Hi again,

I’m testing this and I’m sure I’m missing something. This is my current test code:

DOMAIN = "async_pubsub"

from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_STATE_CHANGED
from homeassistant.core import Event

async def async_setup(hass, config):
    print("Starting async pubsub. ")

    async def send_to_pubsub(event: Event):        
        print(event.data)

    async def start_pubsub(_event):
        print("start_pubsub()")
        hass.bus.listen(EVENT_STATE_CHANGED, send_to_pubsub)

    hass.bus.async_listen_once(
        EVENT_HOMEASSISTANT_STARTED, start_pubsub)    

    return True

But two things:

  1. I get “start_pubsub()” right after “Starting async pubsub” and just before:

homeassistant | [21:21:20] INFO: Home Assistant Core finish process exit code 100

I understood that EVENT_STATE_CHANGED was fired once all the integrations have finished loading. What I want to avoid is listening to all the initial values on the event bus, to avoid having them sent to my queue everytime I restart hass

  1. I get this trace as well:
homeassistant    | 2022-08-29 21:21:30.215 INFO (MainThread) [homeassistant.bootstrap] Home Assistant initialized in 8.81s
homeassistant    | 2022-08-29 21:21:30.216 INFO (MainThread) [homeassistant.core] Starting Home Assistant
homeassistant    | 2022-08-29 21:21:30.229 ERROR (MainThread) [homeassistant] Error doing job: Task exception was never retrieved
homeassistant    | Traceback (most recent call last):
homeassistant    |   File "/config/custom_components/async_pubsub/__init__.py", line 27, in start_pubsub
homeassistant    |     hass.bus.listen(EVENT_STATE_CHANGED, send_to_pubsub)
homeassistant    |   File "/usr/src/homeassistant/homeassistant/core.py", line 917, in listen
homeassistant    |     async_remove_listener = run_callback_threadsafe(
homeassistant    |   File "/usr/src/homeassistant/homeassistant/util/async_.py", line 57, in run_callback_threadsafe
homeassistant    |     raise RuntimeError("Cannot be called from within the event loop")
homeassistant    | RuntimeError: Cannot be called from within the event loop

so it seems that indeed my function is only being called once all is initialized, but why do I get the error? How do I attach to the bus?

thanks!

Ok, 2nd error is because I should call async_listener but still, I’m only getting the events once at start time and then, nothing. Apologies if this is basic stuff!

This is the code I have now:

DOMAIN = "async_pubsub"

from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_STATE_CHANGED
from homeassistant.core import Event

async def async_setup(hass, config):
    print("Starting async pubsub. ")

    async def send_to_pubsub(event: Event):        
        print(event.data)
 
    async def start_pubsub(_event):
        print("start_pubsub()")
        hass.bus.async_listen(EVENT_STATE_CHANGED, send_to_pubsub)
        

    hass.bus.async_listen_once(
        EVENT_HOMEASSISTANT_STARTED, start_pubsub)        

    return True

and I only get events printed after “start_pubsub()” but not if anything is changed once hass has fully booted. Could maybe this be fixed with a simple after_dependencies block on manifest.json?

Not really. Its fired any time the state of an entity changes. Which starts happening as soon as an integration finishes loading its entities. They don’t wait for the rest of the integrations to finish loading to make additional changes in the entities. Waiting for the HA start is a good solution to this problem:

I just want to make sure you’re aware that while this does skip all the junk state changes that are really restore events it also will skip some real state changes which occurred after an integration loaded but before the rest of them did.

Because you’re using the wrong function. You’re using listen, you need to use async_listen from within the event loop.

I’m a bit confused. This sounds correct? Your code prints “start_pubsub()” when it receives the HA started event and then starts printing the data of every event after that. That sounds like what you’re describing? What am I missing?

This is better but only if you actually know what integrations you depend on. It sounds like you want to hand all state change events on from all entities regardless of which integration created them, no? To use after_dependencies you have to specifically list which integrations you want yours to load after. You can’t do that if the answer is “all of them”

Thank you Mike for taking the time to help here.

Yes, apologies, this was my bad. I meant to say EVENT_HOMEASSISTANT_STARTED.

Now with the latest code as shown in the previous post, I see nothing on the logs if, for example, I manually switch on or off a switch on the UI or via one of the remotes I have at home. I’d expect my custom component that is listening to EVENT_STATE_CHANGED to pick it up and print the event to stdout. I’m actually seeing some of the events (mainly sensors?) but not all.

I’m adapting this from the original pubsub integration who listens to the same events.

Ok, async listener is really async, meaning that things do not happen in real time but rather in batches. It took approx. 5 mins to see one of the updates coming.

I’ll try to move forward with what I have now and see if it works as expected.

Thanks!

One more question: Is there any way to make async_listener closer to real time?

So one thing to consider here, you’re testing using print. There’s a couple problems with this:

1 - You should be using python’s logging library. See here for an example of how that works. I just picked a random integration, they all follow the same pattern for initializing and using the logger:

Does this matter? Possibly? HA is a heavily async environment with tons of I/O going on. I would guess the logging library handles that better then OOTB print.

Which brings me to the next point:
2 - You might actually be waiting on the print rather then the event. Printing isn’t actually immediate since its an I/O action. This isn’t obvious in small simple apps but in very complex and I/O heavy apps like HA it becomes more obvious.

So before trying to figure out how to make async_listener faster I would recommend making sure its actually slow. For a simple test rather then printing to the log just fire another event and then listen for that event in HA dev tools. Or at least switch to the python logging library to see if that has an impact.

That was it! Apologies for the newbie mistake and thanks for your help.

1 Like