Skip to content

# HassController

HassController is the interface to Home Assistant systems. It uses Home Assistant's WebSocket API to implement the communications layer. This approach significantly reduces the load of communication on both systems, even in periods of high activity when a lot of data is being exchanged. You will need to make sure that the WebSocket API is enabled in your HomeAssistant configuration. See their documentation for that.

Because Home Assistant sometimes makes breaking changes to its API, only specific version of Home Assistant are supported by this interface. Currently, only Home Assistant versions 2024.1.0 through 2025.10.4 are supported. Versions outside this range may not work at all, or exhibit errant behaviors that are not immediately obvious. Bug reports and fixes for versions outside this range are not accepted/handled.

The only required fields for HassController are `source` and `access_token`:

    reactor:
      # reactor configuration section not shown

    controllers:
    - id: hass
      enabled: true
      implementation: HassController
      name: Home Assistant
      config:
        source: "ws://192.168.0.10:8123"
        access_token: "place long-lived access token within"

Note that the `source` field must contain a URI in the form `ws://ip-address:port`. The `access_token` value is a "long-lived access token" from Home Assistant. To get a LLAT, log in to the Home Assistant UI and click on your username in the bottom-left corner. Then scoll down your profile until you find the "Long-Lived Access Tokens" section. Create a new token there, and copy/paste it into your Reactor controller configuration, keeping it surrounded in quotes as shown above.

Attention

Make sure that the Home Assistant `http` and `websocket_api` components are properly configured and enabled. If you have enabled SSL/TLS in your `http` component configuration (i.e. you specified `ssl_certificate` in your `http` configuration in HA), you must use `wss://` on your `source` value in `reactor.yaml`, and, if you are using a self-signed certificate, you will also need to add `ignore_cert: true` after `source`.

## Mapping Home Assistant Entities to Reactor Entities

HassController tries to do as much of the work of mapping between Home Assistant entities and Reactor entities as possible through configuration files. However, there is some data that is not available through the Home Assistant APIs, so there is a mechanism by which you can either override or enhance HassController's default mapping strategy.

Your overrides and enhancements can be specified in a configuration file you create and manage called `local_hass_devices.yaml`. This file must be located in your `config` subdirectory (where `reactor.yaml` also resides).

### Overriding Entity Name, Capabilities, etc.

To override the name of an entity, or to add a capability that an entity may have that does not register automatically, add the entity to an `entities` section of `local_hass_devices.yaml`, like this:

config/local_hass_devices.yaml

    entities:
      "hass>switch_power_switch":
        name: "My Override Name"
        services:
          - light
          - button
        capabilities:
          - temperature_sensor
        primary_attribute: temperature_sensor.value

In the example above, the entity with the (Reactor) canonical ID hass>switch_power_switch will get given the name shown instead of the name it has in Home Assistant. The `services:` section (optional) tells Reactor what if any additional (Home Assistant) services the entity provides. This may be needed because (sadly) Home Assistant does not enumerate supported services for its entities in its API — there's no way for Reactor to know what services apply except for the one service that is used to form its entity ID. The (optional) `capabilities:` section tells Reactor what additional Reactor capabilities to extend to the entity (if any); in this example, we're saying that this odd entity also has temperature sensor capability. Finally, this example makes the temperature sensor value the default (primary) value for the device.

Services First

If you can, use the `services` section as your first effort to get full entity capabilities. Some Home Assistant services map to multiple Reactor capabilities, so by naming the services first, Reactor is able to correctly find and apply all of those capabilities. If, after naming the services, you don't have a needed native Reactor capability (i.e. not including the `x_` extension capabilities), please report that to me on the SmartHome Community so I can improve the device mapping.

## Filtering Entity Data

Sometimes, Home Assistant entities will update some of their data fields frequently, and this can cause Reactor to throttle rule evaluation when the rule uses that entity in a condition. To reduce "noise" data from Home Assistant, you can use an extended syntax of the `filter_entity` configuration to filter out specific attributes.

Let's say we have an IP camera that has motion detection, and on windy days, the shadows moving in the room from the sun passing through the wind-blown trees causes the motion sensing to trip repeatedly, and that causes spurious evaluation of rules that are using other camera attributes. Using the Developer Tools page in Home Assistant, we can look at all of the state attributes for the camera and discovery it contains these attributes:

    model_name: IP2M-841EW
    brand: Amcrest
    motion_detection: true
    frontend_stream_type: hls
    audio: on
    motion_recording: on
    color_bw: auto
    entity_picture: /api/camera_proxy/camera.cam2?token=
    friendly_name: Cam2
    supported_features: 3

The camera's `motion_detection` attribute is the value that is constantly changing between true and false, so we just want to ignore that attribute. We add the following configuration to the `config` section for our HassController instance:

      - id: hass
        enabled: true
        implementation: HassController
        config:
          source: "ws://192.168.0.10:8123"
          filter_entity:
            "camera.cam2":
              - motion_detection

This tells HassController to ignore the `motion_detection` state attribute on its entity `camera.cam2`. After restarting Reaction, this attribute will no longer be considered for updates, and have no effect on the Reactor entity or attributes — and the excess rule evaluations will stop.

The items under the entity ID (`camera_cam2` in this example) are array elements, and each array element is the name of a state attribute from Home Assistant's list of state attributes for that entity.

Filtering out data isn't the appropriate choice for every situation. If, for example, you have a home weather station that sends updates every few seconds, and you need to use that data rather than filter (discard) it, you can use the features of VirtualEntityController to create a time series and apply some kind of aggregating function, like a weighted average over some period, to make the data more manageable.

### Filtering Out An Entire Entity

Preferred Method: Disable in HA

If your intention is to filter out entities you don't use to reduce the clutter in Reactor's entity list, the best option is to use HA's dashboard (via Settings > Devices & Services > Integrations) to disable those entities that you don't use in either HA or Reactor. This will eliminate the traffic for those entities entirely, and Reactor will not even be aware they exist.

You can filter out an entire entity using `filter_entity` by specifying the key as either the Reactor entity ID (not in canonical form, since the controller being configured is known in context here) or Home Assistant's `domain.entity_id` form, and specifying the value as (boolean) true.

      - id: hass
        enabled: true
        implementation: HassController
        config:
          source: "ws://192.168.0.10:8123"
          filter_entity:
            "camera.cam1": true  # filter out the entire cam1 using HA entity form.
            "sensor_sun": true  # filter out the entire Sun sensor using Reactor entity form.
            #
            # It's still possible to do attribute-level filtering:
            "camera.cam2":
              - motion_detection

A simpler alternate syntax using an array may also be used, but only when attribute-level filtering isn't used at all for any entity:

        config:
          source: "ws://192.168.0.10:8123"
          filter_entity:
            - "camera.cam2"
            - "sensor_sun"

A regular expression can also be used to match entity IDs by using the `/regexp/flags` form. For example, `/^camera/i` would match all entities having IDs beginning with the word camera. The `i` flag, which is recommended for most uses here, provides a case-insensitive match.

To match all entities from a particular integration, you can specify `source:<domain>`, where `<domain>` is the domain provided by an integration. For example, `source:zwave_js` would match all entities originating from the Z-Wave JS integration.

## Handling Home Assistant Events

Some Home Assistant devices send events, and these events are not mapped in Home Assistant to a Home Assistant entity. Since the Home Assistant API does not enumerate these events or the devices that may send them, it is up the user (you) to add event handling for any such events.

There are two ways HassController can handle events as of build 25213. The first is to bind the event to a Reactor Entity: when the event is received by Reactor, rules you define in configuration determine how the attributes of the entity are modified by the event's data. The second, introduced at build 25213, allows you to simply run one or more Reactions in response to receiving the event.

### Binding an Event to a Reactor Entity

Let's say you have an Amcrest camera that can do motion detection and sends motion events, but the HA integration does not create a virtual motion sensor entity to reflect the state of that capability, it only sends events. Here's a typical motion-start event for the camera sent by HA:

    {
        "event_type": "amcrest",
        "data": {
            "camera": "Cam2",
            "event": "VideoMotion",
            "payload": {
                "Code": "VideoMotion",
                "action": "Start",
                "index": "0"
            }
        },
        "origin": "LOCAL",
        "time_fired": "2021-11-16T19:25:10.223198+00:00",
        "context": {
            "id": "9ec3cdea4a733798660e23e2f59b3a2e",
            "parent_id": null,
            "user_id": null
        }
    }

The four highlighted lines above are going to be used to detect this event. We add the following configuration to our HassController's `config` section of `reactor.yaml`:

      config:
        # Additions to config section for Amcrest camera motion:
        event_targets:
         cam2_motion:
            capabilities:
            - motion_sensor
            events:
            - event:
                event_type: amcrest
                data:
                  camera: "Cam2"
                  event: "VideoMotion"
              response:
                "motion_sensor.state":
                  from: "lower(event.data.payload.action)"
                  map:
                    start: true
                    stop: false

This will create a (Reactor) entity with ID `cam2_motion`, having the single capability motion_sensor, that responds to amcrest HA events. If an amcrest event is received that also matches the given data filters (the four highlighted lines above, which match the values in the four highlighted lines of the event JSON shown earlier), the attributes enumerated in `response` will be updated in accordance with their configuration. In this case, that takes the value from the event's data and maps it to a boolean value as required for the attribute.

Each `event` in the `events` section (detailed fully below) contains a number of keys and values that are used to filter events. The subkeys of `event` are compared to the data received. The comparison is literal and case-sensitive; regular expressions are not supported here. In the example above, the `event_type` field and the `data` subfields `camera` and `event` are compared to the event data. If the match is successful, the `response` configuration is processed.

The `response` section is an object the keys of which are attribute names in the target entity. Each attribute name must be fully specified as a `capability_name.attribute_name` pair (e.g. `power_switch.state`). That value for the key is an object containing instructions for setting the attribute value, which are derived from a combination of `from`, `map` and `expr` keys. The `from` key may be used to take a value from the event data received. The top level of the event received is represented by the `event` name in context; for example, using the sample event data received above, one could access the `action` field in the event data as `event.data.payload.action`. The `event` context variable also exists in the `expr` key, so referring to `event.data.payload.action` in the value of either `from` or `expr` produces the same result; the difference is that `from` only allows a member reference in the event data (just the path to a field), where `expr` allows any expression, so you could modify the value before it is assigned to its target attribute. Also note that `expr` and `from` are mutually exclusive; you cannot use both, and if both happen to be configured, only `expr` will be honored. The `map` key accepts from/to pairs; when the derived value matches the from string, it is changed to the to string. This mapping, if configured, is done last, after any `from` or `expr`.

The generalized form of the `event_targets` section is:

          event_targets:    # this section starts the event-receiving entities
            "my_virtual_entity_id":    # Assign an ID to your entity; each entity must have a unique ID
              name: "My Entity Name"   # This is optional but recommended, so you have a friendly name
              capabilities:  # define an array of capabilities to be modified by the event
                - capability_name    # first capability
                - capability_name    # second, etc., as many as you need, but always at least ONE
              events:  # define an array of events that modify capability attributes on the entity
                - event:  # start of an event; each element of the events array begins this way
                    event_type: "type of HomeAssistant event"
                    data:  # optional section, if further matching to the event data is required
                      data_field: "data_value"    # data field to be checked/matched to data_value
                      data_field: "data_value"    # as many as you need, but each data_field must be unique
                  response:  # begin the (required) response section for handling the event
                    "capability.attribute":  # an attribute that the event modifies.
                      expr: "expression"   # expression to get attribute value from event data
                    "capability.attribute":
                      # This shows an alternative to using "expr"/expressions for value handling
                      from: "event.data...."  # dot-reference expression to pull value from event message
                      map:                    # optional, map to modify value
                        value_in: new_value   # if value is value_in, it is mapped to new_value
                        value_in: new_value   # repeat this as needed, but each value_in must be unique
                    # repeat "capability.attribute" section for all attributes modified by the event
                - event:  # start of next event for this entity, if any; same form as above
            "my_second_entity":  # ID of next entity to create/handle events
              events:
                # etc...

Note that in the `expr` expression, the variable `entity` (object) is available with the current data/structure of the target entity. This allows you to access the current value of attributes on the entity while computing the new value, if that's relevant. As stated above, the `event` variable (object) is also available with the full, raw data of the received event.

### Binding Events to Reactions

Another possible way to handle events is to bind them to Reactions, that is, when a matching event is received, one or more Reactions are run. This is also done by configuration, in a section called `event_reactions`. This section is an array of objects containing two required keys: `event` and `reactions`. The `event` section contains the event data to be matched, and the `reactions` section is an array of Reaction IDs to run when matched. Here's an example:

      config:
        # ... other configuration data not shown ...
        event_reactions:
          -
            event:
              event_type: amcrest
              data:
                camera: "Cam2"
                event: "VideoMotion"
            reactions:
              - re-mcx082ds  # Camera 2 Motion Reaction
          -
            event:
              event_type: my_custom_event
            reactions:
              - re-9jfwl2ka  # All lights on
              - re-pk20a7fc  # Lock all doors
            sequential: true

The above example configures handling for two events. The first is the same Amcrest camera motion detection example used earlier, but this time, Reactor will run the Reaction with ID `re-mcx082ds` instead of manipulating attributes on an entity. For the second, a user-defined `my_custom_event` event received from HA will cause the two reactions specified to run.

The form of the `event` section is identical to that of the entity-based configuration (above). The `reactions` section is an array of IDs of the Global or Rule Set-based Reactions to be run.

The optional `sequential` flag determines if the Reactions listed are run sequentially (one at a time) or in parallel. The default is false (parallel).

### Discovering and Logging HA Events

The `event_type` and `data` values are dependent entirely on the event data for the device and integration in HA, and they will vary. The HA and integration documentation are not always entirely clear on the specifics, and some values are device-dependent. To help you find the right values and troubleshoot your event handlers, you can add `log_events: true` to the `config` section of your HassController instance to capture (all) events to a file in your `logs` directory (named `<ctrl>_events.log`, where `<ctrl>` is the ID you set for your HassController instance). The logged events will help you determine what data HA is sending with the event and what the filter values and expressions should/could be.

Large logs!

Turning on event logging will log a lot of data, because Home Assistant processes a lot of events. This setting should be enable only while you are working on an event configuration and turned off immediately after. A restart is required, of course, when the configuration setting is changed.

## Service Call Responses

Some Home Assistant services can return data to Reactor. These services can be accessed using either the entity-specific service call (recommended; see the list of actions for the Reactor entity using the detail view on the Entities page) or using the generic `call_service` action on a specific entity or the controller system entity. A service that has identified as response-capable will present with a "Capture response to" selector (dropdown menu) in the Entity Action(Entity-Action.md) of a Reaction. That will allow the service response to be captured to a variable, which can be a Rule-based variable or global variable, and must be expression-less (i.e. the expression part of the variable definition is empty). The response can then be further processed as needed using Script Action actions in the Reaction, used in substitutions, etc.

## Other Special Configuration for HassController

This section is informational only. It is not usually necessary to specify these values or modify their defaults.

- `timeout` — this value sets the length of time HassController will wait for a successful connection to Home Assistant before throwing an error. The default is 15000 (milliseconds, 15 seconds).

Updated 2025-Oct-29