Expose light to KNX?

Hi, I would like to expose an Home Assistant light (Hue light) to my KNX system in a bi-directional way.

What I have done so far is:

  1. Defined group addresses for the Hue light in ETS
  2. Added a KNX Light entity in the HA KNX integration pointing to those GA
  3. Added a blueprint and related automation to sync the KNX light with the Hue light bi-directionally

If I control the Hue light, the KNX one is synced, and vice-versa. But if I send a on/off message from ETS to either the OnOff GA or the OnOff State GA, nothing happens.

What should I do to make HA react to the values I write from ETS to the GA?

This is my blueprint:

blueprint:
  name: Bidirectional Light Sync (power + brightness + kelvin)
  description: |
    Sincronizza accensione/spegnimento, luminosità e temperatura colore tra due luci in entrambe le direzioni.
    Evita cicli infiniti verificando differenze reali prima di scrivere. Supporta anche la conversione mireds↔kelvin.
  domain: automation
  source_url: https://example.com/asa/bidir_light_sync
  input:
    light_a:
      name: Light A
      description: Prima luce (es. Hue)
      selector:
        entity:
          domain: light
    light_b:
      name: Light B
      description: Seconda luce (es. KNX)
      selector:
        entity:
          domain: light

mode: queued

trigger:
  # --- A → B ---
  - platform: state
    entity_id: !input light_a
    id: a_state
  - platform: state
    entity_id: !input light_a
    attribute: brightness
    id: a_brightness
  - platform: state
    entity_id: !input light_a
    attribute: color_temp_kelvin
    id: a_kelvin
  - platform: state
    entity_id: !input light_a
    attribute: color_temp
    id: a_mireds

  # --- B → A ---
  - platform: state
    entity_id: !input light_b
    id: b_state
  - platform: state
    entity_id: !input light_b
    attribute: brightness
    id: b_brightness
  - platform: state
    entity_id: !input light_b
    attribute: color_temp_kelvin
    id: b_kelvin
  - platform: state
    entity_id: !input light_b
    attribute: color_temp
    id: b_mireds

variables:
  light_a: !input light_a
  light_b: !input light_b
  is_from_a: "{{ trigger.id.startswith('a_') }}"
  src: "{{ light_a if is_from_a else light_b }}"
  dst: "{{ light_b if is_from_a else light_a }}"

  # Stati e attributi sorgente/destinazione
  src_state: "{{ states(src) }}"
  dst_state: "{{ states(dst) }}"
  src_brightness: "{{ state_attr(src, 'brightness') }}"
  dst_brightness: "{{ state_attr(dst, 'brightness') }}"

  # Kelvin calcolato (fallback da mireds)
  src_kelvin: >-
    {% set k = state_attr(src, 'color_temp_kelvin') %}
    {% if k is not none %}
      {{ k }}
    {% elif state_attr(src, 'color_temp') %}
      {{ (1000000 / state_attr(src, 'color_temp')) | round(0) }}
    {% else %}
      {{ none }}
    {% endif %}
  dst_kelvin: >-
    {% set k = state_attr(dst, 'color_temp_kelvin') %}
    {% if k is not none %}
      {{ k }}
    {% elif state_attr(dst, 'color_temp') %}
      {{ (1000000 / state_attr(dst, 'color_temp')) | round(0) }}
    {% else %}
      {{ none }}
    {% endif %}

  # Limiti Kelvin della destinazione
  dst_min_k: >-
    {% set min_k = state_attr(dst, 'min_color_temp_kelvin') %}
    {% if min_k is not none %}
      {{ min_k }}
    {% elif state_attr(dst, 'max_mireds') %}
      {{ (1000000 / state_attr(dst, 'max_mireds')) | round(0) }}
    {% else %}
      {{ none }}
    {% endif %}
  dst_max_k: >-
    {% set max_k = state_attr(dst, 'max_color_temp_kelvin') %}
    {% if max_k is not none %}
      {{ max_k }}
    {% elif state_attr(dst, 'min_mireds') %}
      {{ (1000000 / state_attr(dst, 'min_mireds')) | round(0) }}
    {% else %}
      {{ none }}
    {% endif %}
  clamped_kelvin: >-
    {% if src_kelvin is not none and dst_min_k is not none and dst_max_k is not none %}
      {{ [dst_min_k, [src_kelvin, dst_max_k] | min] | max }}
    {% else %}
      {{ src_kelvin }}
    {% endif %}

condition: []

action:
  - choose:
      # --- POWER ON/OFF ---
      - conditions:
          - condition: template
            value_template: >-
              {{ trigger.id in ['a_state', 'b_state']
                 and trigger.to_state is not none
                 and trigger.to_state.state in ['on','off']
                 and trigger.to_state.state != dst_state }}
        sequence:
          - choose:
              - conditions: "{{ trigger.to_state.state == 'on' }}"
                sequence:
                  - service: homeassistant.turn_on
                    target: { entity_id: "{{ dst }}" }
              - conditions: "{{ trigger.to_state.state == 'off' }}"
                sequence:
                  - service: homeassistant.turn_off
                    target: { entity_id: "{{ dst }}" }

      # --- BRIGHTNESS ---
      - conditions:
          - condition: template
            value_template: >-
              {{ trigger.id in ['a_brightness','b_brightness']
                 and src_brightness is not none
                 and src_brightness != dst_brightness }}
        sequence:
          - service: light.turn_on
            target: { entity_id: "{{ dst }}" }
            data:
              brightness: "{{ src_brightness | int }}"

      # --- KELVIN (da color_temp_kelvin o mireds) ---
      - conditions:
          - condition: template
            value_template: >-
              {{ trigger.id in ['a_kelvin','b_kelvin','a_mireds','b_mireds']
                 and clamped_kelvin is not none
                 and dst_kelvin is not none
                 and clamped_kelvin != dst_kelvin }}
        sequence:
          - service: light.turn_on
            target: { entity_id: "{{ dst }}" }
            data:
              kelvin: "{{ clamped_kelvin | int }}"

Hi :wave:!

My recommendation would be to skip this

as such proxy-entities that can’t get a state from the knx bus have their pitfalls. Just use knx.telegram trigger and knx.send actions instead. Alternatively you could have a look into knx expose.

Or have a look at this blueprint: Advanced control of any light entity from KNX (state, brightness, dim, temperature, color + states feedback)

Thanks, I tried that blueprint, if I control the light from HA it sends the values to KNX, but not the other way around. I don’t even see the on/off command on the BUS monitor though, am I missing something? I do see the messages from the ETS group monitor, just not from the HA one

If knx.send can properly send to the bus, I don’t know.
Maybe ask in the blueprints thread - sharing your configuration.

I don’t think it has anything to do with the blueprint, if I send a GA telegram from ETS I would expect it to show up on the HA KNX Bus Monitor, no matter what :thinking:

So which direction works, and which doesn’t?
It could be a filter-table issue. Are you using routing or tunnelling connection?

I changed the OnOff GA to the one configured on a keypad and it works :thinking: