Bidirectional light sync

:rocket: Zero-Bounce Bidirectional Light Sync Blueprint (State, Brightness & Color Temp)

Tired of sliders bouncing, jumping, or oscillating back and forth when trying to sync two lights bidirectionally in Home Assistant? This blueprint is the ultimate, bulletproof solution for syncing any two lights (e.g., a virtual screen controller like an ESP32 Cheap Yellow Display, and a physical smart bulb) with zero lag and absolutely zero slider bouncing!


:glowing_star: The Problems This Blueprint Solves

1. The Circular "Slider Bounce" Loop

When you sync two dimmable lights bidirectionally, dragging the slider on Light A updates Light B. However, most smart bulbs transition slowly over 1–2 seconds. During this transition, Light B reports intermediate states (e.g., 10%, 30%, 60%) back to Home Assistant. Without protection, your automation thinks: "Aha! The user is adjusting Light B manually!" and pulls Light A's slider back down, causing it to violently jump back and forth.

  • The Solution: This blueprint runs in mode: single with a precise delay: "00:00:01" at the end of each sync. The automation thread remains locked during the transition, quietly ignoring all intermediate feedback loop updates! Once the transition finishes, the lock automatically releases.

2. The none Value API Crash

When a light turns off, Home Assistant deletes its brightness and color_temp_kelvin attributes. Standard sync automations try to pass these none values to the light.turn_on service, causing the Home Assistant API to crash and freeze the sync.

  • The Solution: We use an elegant Jinja2 namespace dictionary builder that dynamically compiles only the attributes that actually exist at that exact moment. If an attribute is none, it is safely omitted from the service call, ensuring 100% crash-free operation!

:hammer_and_wrench: The Blueprint Code

Save this file as zero_bounce_light_sync.yaml in your Home Assistant /config/blueprints/automation/ directory, or import it directly!

blueprint:
  name: Bidirectional Light Sync with Zero-Bounce
  description: >
    Syncs state, brightness, and color temperature bidirectionally between Light A and Light B
    with a 1-second transition lockout to prevent feedback loops and slider bouncing.
  domain: automation
  input:
    light_a:
      name: Light A
      description: The first light (e.g. your CYD virtual light).
      selector:
        entity:
          domain: light
    light_b:
      name: Light B
      description: The second light (e.g. your physical bedroom lamp).
      selector:
        entity:
          domain: light

mode: single
max_exceeded: silent

variables:
  light_a: !input light_a
  light_b: !input light_b

trigger:
  - platform: state
    entity_id: !input light_a
    id: a_changed
  - platform: state
    entity_id: !input light_b
    id: b_changed

condition:
  - condition: template
    value_template: >-
      {{ trigger.from_state is not none and trigger.to_state is not none and
      trigger.from_state.state != trigger.to_state.state or
      trigger.from_state.attributes != trigger.to_state.attributes }}

action:
  - choose:
      - conditions:
          - condition: trigger
            id: a_changed
        sequence:
          - if:
              - condition: state
                entity_id: !input light_a
                state: "on"
            then:
              - target:
                  entity_id: !input light_b
                data: >-
                  {% set b = state_attr(light_a, 'brightness') %}
                  {% set ct = state_attr(light_a, 'color_temp_kelvin') %}
                  {% set result = namespace(data={}) %}
                  {% if b is not none %}{% set result.data = dict(result.data, brightness=b) %}{% endif %}
                  {% if ct is not none %}{% set result.data = dict(result.data, color_temp_kelvin=ct) %}{% endif %}
                  {{ result.data }}
                action: light.turn_on
            else:
              - target:
                  entity_id: !input light_b
                action: light.turn_off
          # Lock execution for 1 second to discard transition feedback loops
          - delay: "00:00:01"
      - conditions:
          - condition: trigger
            id: b_changed
        sequence:
          - if:
              - condition: state
                entity_id: !input light_b
                state: "on"
            then:
              - target:
                  entity_id: !input light_a
                data: >-
                  {% set b = state_attr(light_b, 'brightness') %}
                  {% set ct = state_attr(light_b, 'color_temp_kelvin') %}
                  {% set result = namespace(data={}) %}
                  {% if b is not none %}{% set result.data = dict(result.data, brightness=b) %}{% endif %}
                  {% if ct is not none %}{% set result.data = dict(result.data, color_temp_kelvin=ct) %}{% endif %}
                  {{ result.data }}
                action: light.turn_on
            else:
              - target:
                  entity_id: !input light_a
                action: light.turn_off
          # Lock execution for 1 second to discard transition feedback loops
          - delay: "00:00:01"

:light_bulb: Pro-Tip: Inherit Friendly Names Automatically (For ESPHome/Virtual controllers)

If you are using a virtual screen or controller (like an ESPHome Cheap Yellow Display) as Light A, you can make it automatically inherit the friendly name of your physical bulb (Light B)!

Simply point the ESPHome homeassistant text sensor directly to the physical bulb (light.bedroom1) instead of the virtual entity:

# Inside your ESPHome YAML
text_sensor:
  - platform: homeassistant
    id: ha_name_l1
    entity_id: light.bedroom1   # <-- Point this directly to your physical Light B!
    attribute: friendly_name
    on_value:
      then:
        - lvgl.label.update:
            id: l1_name_label
            text: !lambda |-
              if (x.empty()) return id(cyd_l1)->get_name();
              return x;

Now, whenever you rename your physical bulb in Home Assistant, the virtual display will automatically rename its screen buttons to match!


:star: Credits & Feedback

If you find this blueprint helpful, please leave a comment or drop a :star:! Let me know if you have any questions or optimizations.

Hello Avishyf,
Thanks for contributing to the community with a new Blueprint.
I have a suggestion for you. Many people who are not familiar with directory structures will have problems installing this without the Home Assistant MY tools.
Adding a MY link for this Blueprint to your top post would help them a lot.
Here is the link to make that.
Create a link – My Home Assistant

Note: if the original is in the forums here, it has to be in the top post in the topic and has to be the only code block there or the link will not work.