Skip to content

Event based triggers

Craig Barratt edited this page Aug 5, 2020 · 8 revisions

Event Based Triggers

Here's more detail on event triggers and some examples of using them.

Warning: examples have not been tested yet! Should get to that in the next couple of days.

HASS Standard Triggers

Here's a list of standard HASS triggers. The names are constants that are defined in homeassistant.const; for example the name EVENT_CALL_SERVICE is defined to be "call_service".

Event name What it means
EVENT_CALL_SERVICE A service is being called
EVENT_COMPONENT_LOADED A component has been loaded
EVENT_CORE_CONFIG_UPDATE A configuration update has happened
EVENT_HOMEASSISTANT_CLOSE HASS is closing
EVENT_HOMEASSISTANT_START HASS is starting
EVENT_HOMEASSISTANT_STARTED HASS has started
EVENT_HOMEASSISTANT_STOP HASS is stopping
EVENT_HOMEASSISTANT_FINAL_WRITE HASS is about to stop
EVENT_LOGBOOK_ENTRY A logbook entry is added
EVENT_PLATFORM_DISCOVERED Platform discovered
EVENT_SERVICE_REGISTERED A service has been registered
EVENT_SERVICE_REMOVED A service has been removed
EVENT_STATE_CHANGED A state has changed
EVENT_THEMES_UPDATED Themes are updated
EVENT_TIMER_OUT_OF_SYNC Time is out of sync
EVENT_TIME_CHANGED A 1 second heartbeat

Only a handful of these will typically be of interest. The EVENT_HOMEASSISTANT_START and EVENT_HOMEASSISTANT_STARTED events happen before pyscript starts. The startup @time_trigger (with no arguments) is actually triggered by the EVENT_HOMEASSISTANT_STARTED event. Similarly, most components will be loaded and services will be registered during startup before EVENT_HOMEASSISTANT_STARTED, so those corresponding events won't occur after HASS is started. The EVENT_TIME_CHANGED is just a 1 second heartbeat and you are better off using a periodic @time_trigger.

If you want to eavesdrop on service calls, you could have a trigger on EVENT_CALL_SERVICE. The parameters are:

  • domain
  • service
  • service_data

This trigger will log a message on every service call:

from homeassistant.const import EVENT_CALL_SERVICE

@event_trigger(EVENT_CALL_SERVICE) 
def monitor_service_events(domain=None, service=None, service_data=None):
    log.info(f"{domain}.{service} called with service_data={service_data}")

Trigger conditions can be added using a Python string expression in the 2nd argument. For example, to only trigger when domain is lights:

@event_trigger("call_service", "domain == 'lights'") 
def monitor_lights_service_events(service=None, service_data=None):
    log.info(f"lights.{service} called with service_data={service_data}")

In this example the domain function argument was removed since it will always be lights, and the event name was replaced by its value instead of importing it (although best practices would be to import and use the constant name).

You could also trigger on state change events. However, it's much better and more efficient to use @state_trigger instead. But if you want to see some or all state changes (rather than specific ones) you could trigger on EVENT_STATE_CHANGED. The parameters are:

  • entity_id - the string name of the state variable
  • new_state - the new value and attributes
  • old_state - the old value and attributes

Here's an example that triggers on all state changes for state variables whose names starts with "lights.":

from homeassistant.const import EVENT_STATE_CHANGED

@event_trigger(EVENT_STATE_CHANGED, "entity_id.startswith('lights.')") 
def monitor_state_change(entity_id=None, new_state=None, old_state=None):
    log.info(f"entity {entity_id} changed from {old_state} to {new_state}")

If you run this test you will see that old_state and new_state are objects. The actual string value is new_state.state and attributes are new_state.attributes, which is a dict. So you could update the log message to

from homeassistant.const import EVENT_STATE_CHANGED

@event_trigger(EVENT_STATE_CHANGED, "entity_id.startswith('lights.')") 
def monitor_state_change(entity_id=None, new_state=None, old_state=None):
    old_value = old_state.state if old_state else None
    log.info(f"entity {entity_id} changed from {old_value} to {new_state.state} and attributes are now {new_state.attributes}")

Note that old_state could be None, which the code checks for.

Be careful about what event triggers you add, since every event of that type across HASS will cause your conditions to be checked and potentially your function to be run. A large HASS implementation will have a lot of state changes and service calls. Also, please don't set a state variable inside a function that is trigged by EVENT_STATE_CHANGED, or make a service call that is triggered by EVENT_CALL_SERVICE, unless the trigger condition is False in those cases. Otherwise bad things will happen...

Finally, if you are not sure what data is attached to an event, create a trigger with no additional conditions, and use the kwargs form to capture all the arguments in a dict:

from homeassistant.const import EVENT_STATE_CHANGED

@event_trigger(EVENT_STATE_CHANGED) 
def monitor_state_change(**kwargs):
    log.info(f"got EVENT_STATE_CHANGED with kwargs={kwargs}")

User Defined Events

HASS allows user-defined events to be fired. They have a string name, and optional parameters. You can use @event_trigger in just the same way as the examples above.

TODO: add some examples...