Help request - switching different lights based on value in an event

Hi there,

I’m in the process of moving from OpenHAB (+Node Red) to Home Assistant (+Node Red) and am struggling with understanding how to translate an event value to an action to be performed on different lights. Hoping you can help!!

My installation has an old KNX/EIB system with devices (input sensors on buttons, dimmers, and relays to control lights) set up in KNX Easy Mode. This means I can’t use the KNX integration in its ‘normal’ mode (ie can’t read existing states, can’t control dimming levels, can’t use normal way to turn on the relays, etc etc).

What I can do instead is receive events when a button is pressed and send events to turn lights on/off. This works well in the existing solution, but I’m really struggling to get my head around how to do it in HASS.

Ideally what I’d like to do is something along the lines of:

  • Set up virtual (template?) ‘lights’ in HASS to represent each of my 23 physical lights
  • Bind Homekit to each ‘light’ so that I can see their status & turn them on/off with Homekit
  • Have the ‘lights’ receive events from the KNX bus when physical buttons are pressed to reflect the new on/off status
  • Have the ‘lights’ send events to the KNX bus to turn physical lights on/off when turned on/off in Homekit
  • Also be able to use automations, Node Red, etc turn the ‘lights’ on/off as required

The events I receive in HASS from the KNX bus look like:

{
    "event_type": "knx_event",
    "data": {
        "data": [
            190
        ],
        "destination": "1/0/10",
        "direction": "Incoming",
        "value": 190,
        "source": "0.15.1",
        "telegramtype": "GroupValueWrite"
    },
    "origin": "LOCAL",
    "time_fired": "2022-01-17T00:24:49.138103+00:00",
    "context": {
        "id": "a5e99080a7af3d75a51e6aa90cc124ef",
        "parent_id": null,
        "user_id": null
    }
}

The key pieces of data to look for are destination and value.

Destination is the “group address” of one of the lights on the KNX bus, and typically looks like ‘1/0/1’, ‘1/0/3’… etc (23 different group addresses for my lights)

The on/off state of the light is reflected in bit 1 (starting from bit 0, big-endian) of the value byte. Simple logic such as ((trigger.event.data.value | bitwise_and(2)) > 0) gives you the on/off state.

So, ideally, what I would have is some mechanism whereby, whenever a KNX event is received, the on/off state and light address are decoded, and that state is sent to the appropriate light. I would hope that either this can be done by a function that looks up the correct ‘light’ from an address table, or done directly by the ‘lights’ looking for events that correspond to their group address.

On the send side I guess the light can invoke a script with address & state values which then sends the appropriate command on the KNX bus (again, because of the type of KNX implementation, I can’t use the integration built-in functionality. Instead I need to send an event to the respective group address with a byte value of either ‘2’ (0x02) for ‘on’ or ‘0’ (0x00) for 'off.

I also -if possible- need to address the issue of ‘command duplication’. What I want to avoid if possible is the situation whereby, if a light is turned on/off by a physical KNX button press, that event changes the state of the corresponding ‘light’ in HASS, which then triggers a send event for the new state back to the KNX bus (that is, the destination light sees, for example, ‘on’ from button 5, and then also another ‘on’ from HASS). I can live with it if it’s unavoidable, but it sometimes causes funny issues with bus contention if several things happen all at once, so would prefer that we didn’t do that…

I think I sort of understand how to do the send side, but really need help to work out how to do the receive side. My feeling is that I need an automation that detects the event that fires a script that translates the destination & state, that then updates the appropriate ‘light’… but I’m really not sure that’s the ‘best’ way and sort of lost with how to do all that ‘properly’

Any hints are much appreciated!!! Thank you :slight_smile:

Hi :wave:!

I guess you are looking for some combination of

  • Template light which can be used to send to the bus via knx.send service and
  • an automations using eg. trigger id and choose to set their states from the bus - not sure if the value templates in template lights can work directly with events. For 23 of them you could look into defining your own blueprint - see KNX - relative dimming for lights blueprint · GitHub for inspiration. I’d start with an automation for one and turn it into a blueprint when it is working fine to populate it easily to the others. If you need to change / fix something later you will only have to do it in one place.

You should only update the state in HA on received telegrams. Setting a new state is a different service (light.turn_on) which doesn’t need to be invoked then - only when you trigger it from HA.

Forwarding light entities to HomeKit then is trivial - see HomeKit Bridge - Home Assistant

It seems like you are out for some trial and error :grimacing: If you need help regarding the KNX integrtion / library side of things you are welcome to join the xknx discord server.
Automation and blueprint experts are found here or in HAs discord server.

Good luck!

Thanks farmio!

I’m slowly getting there. One question - how can I set the state of an entity (a template light) from an automation?

As you say, the light.turn_on/off causes events to be fired.

I can’t find a simple way to just change the state?

Yeah, thats the tricky part.
As I said

not sure if the value templates in template lights can work directly with events

if this worked, it would be straight forward. Maybe some template wizard can answer this (HA discord).

Assuming it doesn’t work you could (there are probably other solutions too, but this is the first that comes to my mind) create templates (trigger based template sensors / binary_sensors for each function (state, brightness, etc.) and use their state as value template for the template lights.
An outgoing telegram would then also trigger the events and change the event based template and thus the state of the template light.

So… I finally did it. It took me almost two days & was an incredibly painful learning curve (many times I just wanted to abandon the effort and do it in Node Red instead, but I persisted…)

And, in the end, it’s actually pretty simple and gives a really easy to configure & flexible result. All I need to do to add more lights is simply copy/paste/edit an entry in configuration.yaml - everything else adapts automatically.

Done as follows -

Excerpt of the relevant parts from configuration.yaml:

python_script:

homekit:
  filter:
    include_domains:
      - light

knx:
  event:
    - address:
        - "*/*/*"
      type: "1byte_unsigned"


light:
  - platform: template
    lights:
      knx_1_0_5:
        friendly_name: "Front Corridor Stairwell Light"
        unique_id: front_corridor_stairwell_light
        turn_on:
          service: script.turn_on_eib_light
          data:
            group_address: 1/0/5
        turn_off:
          service: script.turn_off_eib_light
          data:
            group_address: 1/0/5
      knx_1_0_11:
        friendly_name: "Family Room Pendant Light"
        unique_id: family_room_pendant__light
        turn_on:
          service: script.turn_on_eib_light
          data:
            group_address: 1/0/11
        turn_off:
          service: script.turn_off_eib_light
          data:
            group_address: 1/0/11



Some points of interest:

  • Yes, I am using a python script, so enabling that integration. Using the fabulous set_state.py script by Rod Payne (instructions for setting that up in Home Assistant are very clear near the bottom of the first page). Note: I use this rather than the ‘light.turn_on/off’ service as this avoids sending another event when the state of a virtual light is updated from the KNX bus (otherwise I’d get a ‘light x on’ message from the KNX bus, update the light in Home Assistant, and then send another ‘light x on’ message back to KNX, which isn’t great…). It would be really good if Home Assistant allowed us to natively differentiate between state changes and events/commands, but sigh it doesn’t (yet)…
  • The simple Homekit binding filter ensures that it only automatically creates/removes lights in Homekit
  • I set up the KNX integration to generate events for all group address messages received. The byte containing the on/off state is delivered to ‘value’ as 1 byte unsigned (due to the non-standard encoding)
  • These lights are only ‘on’ or ‘off’ - no ability to dim (yet) because that relies on how long you press the switches (!)
  • Only showing two lights for brevity, but once you’ve defined one it’s very simple to copy/pasta/edit to define the others
  • Key point - naming convention of the entity ID reflects that of the associated knx group address, and this makes automation and scripting super simple

Which brings me to the next part - automations.yaml:

- id: '1642399478189'
  alias: Update state of lights from KNX bus
  description: Update state of a light when updated from the KNX bus.  Just change
    state so that no event is triggered
  trigger:
  - platform: event
    event_type: knx_event
  condition:
  - condition: template
    value_template: '{{ trigger.event.data.source != ''0.0.0'' }}'
  action:
  - service: python_script.set_state
    data:
      entity_id: '{{ "light.knx_" ~ (trigger.event.data.destination|replace("/","_"))  }}'
      state: '{% if (trigger.event.data.value | bitwise_and(2)) > 0 %} on {% else
        %} off {% endif %}'
  mode: queued
  max: 10

So here, whenever I receive a knx_event which doesn’t originate from my own interface, I invoke the set_state script to target the corresponding virtual device in Home Assistant and update its state accordingly. The nice thing is that it automatically adapts to the lights as I configure them in configuration.yaml without needing to change anything in the script.

Notes:

  • set_state ignores entities that are not defined (but puts out a nice log message which makes finding any devices I haven’t already set up very easy to find - even tells me their correct group addresses :slight_smile:)
  • entity_id is created from the group address I receive from the KNX bus (replacing the ‘/’ with ‘_’ because yaml/home assistant don’t like the slashes…) - and this correlates with the entity ID naming convention used in configuration.yaml
  • and state is just created by checking the appropriate bit in the message I receive to see if the light is on or off

Finally, here’s the scripts.yaml file that the template lights invoke whenever they are turned on or off:

turn_on_eib_light:
  alias: Turn on EIB light
  sequence:
  - service: knx.send
    data:
      address: '{{ group_address }}'
      payload: 2
  mode: queued
  icon: mdi:lightbulb-on-outline
  max: 10

turn_off_eib_light:
  alias: Turn off EIB light
  sequence:
  - service: knx.send
    data:
      address: '{{ group_address }}'
      payload: 0
  mode: queued
  icon: mdi:lightbulb-outline
  max: 10

These scripts simply send ‘on’ or ‘off’ values to the group addresses corresponding to the lights defined in configuration.yaml.

And that’s about it. Simples :slight_smile:

Hope this helps someone!! Let me know if it does :grinning_face_with_smiling_eyes:

If not all your telegrams have 1 byte length this will yield some errors. You can use the data[0] key (it’s first element as it is a list for non-binary telegrams) instead of value to avoid this.

Consider using the direction key instead of source - the source should not be 0.0.0 - this could possibly break in future updates of xknx.

Thanks @farmio for the suggestions!

I have modified configuration.yaml to:

knx:
  event:
    - address:
        - "*/*/*"

and automations.yaml to:

- id: '1642399478189'
  alias: Update state of lights from KNX bus
  description: Update state of a light when updated from the KNX bus.  Just change
    state so that no event is triggered
  trigger:
  - platform: event
    event_type: knx_event
  condition:
  - condition: template
    value_template: '{{ trigger.event.data.direction == ''Incoming'' }}'
  action:
  - service: python_script.set_state
    data:
      entity_id: '{{ "light.knx_" ~ (trigger.event.data.destination|replace("/","_"))  }}'
      state: '{% if (trigger.event.data.data[0] | bitwise_and(2)) > 0 %} on {% else
        %} off {% endif %}'
  mode: queued
  max: 10

Seems to be working well and gives a simpler & more robust configuration :slight_smile: