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 2023.6.0 through 2024.11.3 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 Hass), 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:

``` yaml hl_lines=6-8 - 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.

## 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.

For example, 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:

``` json hl_lines="2-5"
{
    "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.

Note that the event_type and data values are dependent entirely on the event data for the device and integration in Hass, and they will vary. You can add log_events: true to the config section of your HassController instance to capture events to a file in your logs directory, to help you determine what data Hass is sending with the event and what the filter values and expressions should be.

Large logs!

Turning on event logged 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.

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.

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 in a variable, which can be a rule-based variable or global variable (expression-less). 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 2024-Nov-24