# Light Color Mapper # Syncs light colors in real-time from color sensor entities. # See README.md for detailed documentation. # # Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com) blueprint: name: "Custom: Light Color Mapper" description: > Maps color sensor states to lights in real-time. Sensors and lights are paired by list index (sensor 0 → light 0, etc.). Supports FPS throttling, brightness override, and configurable behavior when sensors are unavailable. domain: automation # =========================================================================== # INPUT CONFIGURATION # =========================================================================== input: # ------------------------------------------------------------------------- # Control # ------------------------------------------------------------------------- control_group: name: "Control" collapsed: false input: control_entity: name: Control Entity description: "Switch or input_boolean that enables/disables this automation." selector: entity: domain: - input_boolean - switch turn_off_on_disable: name: Turn Off Lights on Disable description: "Turn off all mapped lights when the control entity is switched off." default: true selector: boolean: # ------------------------------------------------------------------------- # Devices # ------------------------------------------------------------------------- devices_group: name: "Devices" collapsed: false input: color_sensors: name: Color Sensors description: > List of color sensor entities. Each sensor's state is a hex color string (e.g., #FF8800) with attributes: r, g, b, brightness, rgb_color. Sensors are mapped to lights by list index. selector: entity: domain: sensor multiple: true lights: name: Lights description: > List of light entities. Each light is paired with the sensor at the same list index (sensor 0 → light 0, sensor 1 → light 1, etc.). selector: entity: domain: light multiple: true # ------------------------------------------------------------------------- # Settings # ------------------------------------------------------------------------- settings_group: name: "Settings" collapsed: false input: max_fps: name: Maximum FPS description: > Maximum updates per second. With mode: restart, a trailing delay throttles updates naturally — new state changes during the delay restart the automation with fresh data. Set to 0 for unlimited. default: 0 selector: number: min: 0 max: 60 mode: slider brightness_override: name: Brightness Override description: > Override brightness for all lights (0–255). Set to 0 to use each sensor's brightness attribute instead. default: 0 selector: number: min: 0 max: 255 mode: slider min_brightness: name: Minimum Brightness description: > Minimum brightness for all lights (1–255). If the sensor or override brightness is below this value, this minimum is used instead. Prevents lights from becoming too dim to see. default: 5 selector: number: min: 1 max: 255 mode: slider none_action: name: When Sensor is Unavailable description: "Action to take when a sensor state is None, unknown, or unavailable." default: "turn_off" selector: select: options: - label: "Turn Off Light" value: "turn_off" - label: "Keep Last Color" value: "keep_last" - label: "Set Default Color" value: "default_color" default_color: name: Default Color description: > Color to apply when a sensor is unavailable. Only used when "When Sensor is Unavailable" is set to "Set Default Color". default: [255, 255, 255] selector: color_rgb: # ------------------------------------------------------------------------- # Callback Actions # ------------------------------------------------------------------------- actions_group: name: "Callback Actions" collapsed: true input: post_update_action: name: Post-Update Action description: > Custom action to run after all sensor-light pairs have been processed. Runs once per update cycle, after all lights are set. Leave empty to do nothing. default: [] selector: action: # ------------------------------------------------------------------------- # Debug # ------------------------------------------------------------------------- debug: name: "Debug" collapsed: true input: enable_debug_notifications: name: Enable Debug Notifications description: > Send persistent notifications showing sensor states and light updates for troubleshooting. default: false selector: boolean: # Restart mode ensures the latest sensor state always wins. # Combined with a trailing delay (FPS throttle), incoming state changes # during the delay cancel the current run and restart with fresh data. mode: restart # ============================================================================= # TRIGGERS # ============================================================================= trigger: # Any color sensor changes state - platform: state entity_id: !input color_sensors id: "sensor_changed" # Control entity toggled on/off - platform: state entity_id: !input control_entity id: "control_changed" # ============================================================================= # VARIABLES # ============================================================================= variables: # --------------------------------------------------------------------------- # Input References # --------------------------------------------------------------------------- control_entity: !input control_entity turn_off_on_disable: !input turn_off_on_disable color_sensors: !input color_sensors lights: !input lights max_fps: !input max_fps brightness_override: !input brightness_override min_brightness: !input min_brightness none_action: !input none_action default_color: !input default_color enable_debug_notifications: !input enable_debug_notifications post_update_action: !input post_update_action # --------------------------------------------------------------------------- # Computed Values # --------------------------------------------------------------------------- # Use the shorter list length to avoid index-out-of-range errors pair_count: "{{ [color_sensors | length, lights | length] | min }}" # ============================================================================= # ACTIONS # ============================================================================= action: # --------------------------------------------------------------------------- # Debug Logging # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ enable_debug_notifications }}" sequence: - service: persistent_notification.create data: title: "Light Color Mapper Debug" message: > **Trigger:** {{ trigger.id }} **Control:** {{ states(control_entity) }} **Pairs:** {{ pair_count }} **FPS:** {{ max_fps }} **Brightness Override:** {{ brightness_override }} **Min Brightness:** {{ min_brightness }} **Sensor States:** {% for i in range(pair_count | int) %} {{ i }}: {{ color_sensors[i] }} = {{ states(color_sensors[i]) }} (rgb: {{ state_attr(color_sensors[i], 'rgb_color') }}, brightness: {{ state_attr(color_sensors[i], 'brightness') }}) {% endfor %} # --------------------------------------------------------------------------- # Check Control Entity # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ not is_state(control_entity, 'on') }}" sequence: # Turn off all mapped lights if configured - choose: - conditions: - condition: template value_template: "{{ turn_off_on_disable }}" sequence: - service: light.turn_off target: entity_id: "{{ lights[:pair_count | int] }}" - stop: "Control entity is off" # --------------------------------------------------------------------------- # Process Each Sensor-Light Pair # --------------------------------------------------------------------------- - repeat: count: "{{ pair_count | int }}" sequence: - variables: idx: "{{ repeat.index - 1 }}" current_sensor: "{{ color_sensors[idx] }}" current_light: "{{ lights[idx] }}" sensor_state: "{{ states(current_sensor) }}" is_unavailable: "{{ sensor_state in ['None', 'none', 'unknown', 'unavailable', 'undefined'] or sensor_state | length == 0 }}" - choose: # Sensor is unavailable — apply configured action - conditions: - condition: template value_template: "{{ is_unavailable }}" sequence: - choose: # Turn off the light - conditions: - condition: template value_template: "{{ none_action == 'turn_off' }}" sequence: - service: light.turn_off target: entity_id: "{{ current_light }}" # Set default color - conditions: - condition: template value_template: "{{ none_action == 'default_color' }}" sequence: - service: light.turn_on target: entity_id: "{{ current_light }}" data: rgb_color: "{{ default_color }}" brightness: "{{ [brightness_override | int if brightness_override | int > 0 else 255, min_brightness | int] | max }}" # keep_last — do nothing # Sensor has valid state — apply color to light default: - variables: sensor_rgb: "{{ state_attr(current_sensor, 'rgb_color') | default([255, 255, 255]) }}" sensor_brightness: "{{ state_attr(current_sensor, 'brightness') | default(255) }}" target_brightness: "{{ [brightness_override | int if brightness_override | int > 0 else sensor_brightness | int, min_brightness | int] | max }}" - service: light.turn_on target: entity_id: "{{ current_light }}" data: rgb_color: "{{ sensor_rgb }}" brightness: "{{ target_brightness }}" # --------------------------------------------------------------------------- # Post-Update Callback # --------------------------------------------------------------------------- - choose: - conditions: - condition: template value_template: "{{ post_update_action | length > 0 }}" sequence: !input post_update_action # --------------------------------------------------------------------------- # FPS Throttle # --------------------------------------------------------------------------- # With mode: restart, new triggers during this delay cancel and restart # the automation, effectively capping updates to max_fps per second. - choose: - conditions: - condition: template value_template: "{{ max_fps | int > 0 }}" sequence: - delay: milliseconds: "{{ (1000 / (max_fps | int)) | int }}"