Files
haos-blueprints/Zigbee/MQTT Light Control.yaml
alexei.dolgolyov 31650380ea
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
Add transition duration, scene activation, JSON presets, and group/area targeting to MQTT Light Control
- Add configurable transition duration (0-10s) for smooth light changes
- Add scene activation support with 4 configurable scene slots
- Change preset format from key:value;key:value to JSON with expanded keys
- Add light group entity and area-based targeting options
2026-01-25 04:19:40 +03:00

2858 lines
116 KiB
YAML

# =============================================================================
# MQTT Light Control Blueprint for Home Assistant
# =============================================================================
# This blueprint provides extended light control functionality via MQTT devices
# (typically Zigbee controllers through zigbee2mqtt).
#
# Features:
# - Control multiple lights via MQTT topics
# - Sequential and "all at once" turn on/off actions
# - Brightness control (increment, hold, list-based, min/max)
# - Color temperature control (increment, hold, list-based)
# - RGB/Hue control (increment, hold, list-based)
# - Preset list support (JSON format) for quick scene switching
# - Scene activation support (up to 4 scenes)
# - Light group and area targeting
# - Configurable transition duration for smooth changes
# - Alternative light support (control secondary lights when primary are off)
# - Persistent state storage via input_text entity
# - Custom action callbacks for extending functionality
#
# Author: Alexei Dolgolyov (dolgolyov.alexei@gmail.com)
# =============================================================================
blueprint:
name: "Custom: MQTT Light Control"
description: Extended light control blueprint using MQTT device(s)
domain: automation
# ===========================================================================
# INPUT CONFIGURATION
# ===========================================================================
input:
# -------------------------------------------------------------------------
# MQTT Device Configuration
# -------------------------------------------------------------------------
# Configure up to 4 MQTT topics for different controllers/devices
# that can trigger this automation.
Devices:
name: "Devices"
collapsed: false
input:
mqtt_topic:
name: MQTT Topic 1
description: The MQTT topic of the Zigbee device (e.g., zigbee2mqtt/my_controller_1)
selector:
text:
mqtt_topic2:
name: MQTT Topic 2 (Optional)
description: >
Additional MQTT topic for a second controller.
Leave as default placeholder if not using.
default: "blueprint/disabled/mqtt_light_control_2"
selector:
text:
mqtt_topic3:
name: MQTT Topic 3 (Optional)
description: >
Additional MQTT topic for a third controller.
Leave as default placeholder if not using.
default: "blueprint/disabled/mqtt_light_control_3"
selector:
text:
mqtt_topic4:
name: MQTT Topic 4 (Optional)
description: >
Additional MQTT topic for a fourth controller.
Leave as default placeholder if not using.
default: "blueprint/disabled/mqtt_light_control_4"
selector:
text:
# -------------------------------------------------------------------------
# Persistent State Configuration
# -------------------------------------------------------------------------
# Used to store automation state (brightness direction, list indices, etc.)
# in JSON format for features that need to remember state between triggers.
PersistentState:
name: "Persistent State"
collapsed: false
input:
automation_state_entity:
name: Automation state entity
description: >
The `input_text` entity will be used to store state of the automation
in JSON format. Required for brightness list/brightness direction/color
temp list functionality. Doesn't require any initial state. For now each
automation must have its personal entity.
default: null
selector:
entity:
domain:
- input_text
automation_state_key_override:
name: Automation state key override
description: >
If specified use this key as JSON key for persistent state structure.
By default the key will be resolved automatically using light ids.
Don't use it if you don't understand its meaning.
default: ''
selector:
text:
# -------------------------------------------------------------------------
# Target Lights Configuration
# -------------------------------------------------------------------------
# Define which lights/switches this automation should control.
# Both direct entity selection and helper-based selection are supported.
Light:
name: "Target Lights"
collapsed: false
description: Allows to specify lights to be controlled. All of the options can be used simultaneously.
input:
target_lights:
name: Target Light (optional)
description: The lights to be controlled directly
default: []
selector:
entity:
domain:
- light
- switch
multiple: true
target_lights_helper:
name: Target Lights Helper (optional)
description: Input helper (e.g., input_text) containing the light entity ID.
default: ""
selector:
entity:
domain:
- input_text
multiple: true
target_light_group:
name: Target Light Group (optional)
description: >
A light group entity to control. The group will be controlled as
a single entity. Can be used alongside individual lights.
default: null
selector:
entity:
domain: light
target_area:
name: Target Area (optional)
description: >
An area to control. All lights in the area will be discovered
and controlled. Provide the area ID (e.g., "living_room").
default: ""
selector:
text:
# -------------------------------------------------------------------------
# Alternative Lights Configuration
# -------------------------------------------------------------------------
# Alternative lights are controlled when all primary lights are OFF.
# Useful for controlling secondary lighting (e.g., accent lights).
Alternative_Light:
name: "Alternative Lights"
collapsed: false
description: >
Allows to specify alternative lights to be controlled.
All of the options can be used simultaneously.
input:
alternative_light_1:
name: Alternative Light 1 (optional)
description: >
Light to be toggled using `Turn Off` button when all primary lights
are turned off. `Turn On` and `Turn On All` action ids must be different.
All other actions (like brightness/temperature/color control will be supported).
default: null
selector:
entity:
domain:
- light
- switch
alternative_light_1_toggle_callback:
name: Alternative Light 1 Toggle Callback
description: "Callback action triggered on alternative light 1 toggle"
default: []
selector:
action: {}
alternative_light_2:
name: Alternative Light 2 (optional)
description: >
Light to be toggled using `Turn Off All` button when all primary lights
are turned off. `Turn Off` and `Turn Off All` action ids must be different.
All other actions (like brightness/temperature/color control will be supported).
default: null
selector:
entity:
domain:
- light
- switch
alternative_light_2_toggle_callback:
name: Alternative Light 2 Toggle Callback
description: "Callback action triggered on alternative light 2 toggle"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Synchronization Settings
# -------------------------------------------------------------------------
# Controls how light options (brightness, color) are synchronized
# when turning on multiple lights.
Synchronization_Group:
name: "Synchronization"
collapsed: true
input:
best_source_light_for_options:
name: Best source light for options (optional)
description: >
Light that will be used as options source (only if light is enabled
at action time). By default will use the last resolved enabled light.
default: null
selector:
entity:
domain: light
multiple: false
apply_common_options_to_newly_enabled_light:
name: Apply common options to newly enabled light
description: >
If ON then newly enabled light will reference options from the last
enabled light or best source light (if specified).
default: true
selector:
boolean: {}
# -------------------------------------------------------------------------
# Action Step Configuration
# -------------------------------------------------------------------------
# Defines how step values are determined for brightness/color changes.
# Step can come from MQTT payload, entity, or fallback value.
ActionStep_Group:
name: "Action Step"
collapsed: true
input:
action_step_payload_key:
name: Action Step Payload Key (optional)
description: A key used to extract step size from MQTT json action payload
default: action_step_size
selector:
text:
action_step_entity:
name: Action Step Entity (optional)
description: >
A sensor entity that defines the brightness/color temperature/hue step
(i.e. action from device)
default: null
selector:
entity:
domain:
- sensor
- input_number
action_step_fallback:
name: Action Step Fallback (optional)
description: A value that will be used in case if action step is not resolved or is zero.
default: 51
selector:
number:
min: 0
max: 255
step: 1
unit_of_measurement: "level"
# -------------------------------------------------------------------------
# Turn On Actions
# -------------------------------------------------------------------------
Action_Group_TurnOn:
name: "Actions: Turn On"
collapsed: true
input:
action_turn_on:
name: Turn On Action ID
description: >
Action ID for sequential turn on. Can be equal to any of turn off
actions ids, so the action will only trigger if light is OFF.
default: 'on'
selector:
text:
action_turn_on_callback:
name: Callback
description: "Callback action triggered on common turn on event"
default: []
selector:
action: {}
Action_Group_TurnOnRepeat:
name: "Actions: Repeat Turn On"
collapsed: true
input:
action_for_repeated_turn_on:
name: Repeat Turn On Action ID
description: If specified will publish MQTT message with the other action identifier.
default: repeat_on
selector:
text:
action_repeat_turn_on_callback:
name: Repeat Callback
description: >
Repeat callback action called if `Turn On Action` arrives
when all the lights are already on.
default: []
selector:
action: {}
Action_Group_TurnOnAll:
name: "Actions: Turn On All"
collapsed: true
input:
action_turn_on_all:
name: Turn On All Action ID
description: >
Action ID for all lights turn on. Can be equal to any of turn off
actions ids, so the action will only trigger if light is OFF.
default: on_all
selector:
text:
action_turn_on_all_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
Action_Group_TurnOnAllRepeat:
name: "Actions: Repeat Turn On All"
collapsed: true
input:
action_for_repeated_turn_on_all:
name: Repeat Turn On All Action ID
description: If specified will publish MQTT message with the other action identifier.
default: repeat_on_all
selector:
text:
action_repeat_turn_on_all_callback:
name: Repeat Callback
description: >
Repeat callback action called if `Turn On All Action` arrives
when all the lights are already on.
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Turn Off Actions
# -------------------------------------------------------------------------
Action_Group_TurnOff:
name: "Actions: Turn Off"
collapsed: true
input:
action_turn_off:
name: Turn Off Action ID
description: >
Action ID for sequential turn off. Can be equal to any of turn on
actions ids, so the action will only trigger if at least one of
the provided lights is ON.
default: off
selector:
text:
action_turn_off_callback:
name: Callback
description: "Callback action triggered on common turn off event"
default: []
selector:
action: {}
Action_Group_TurnOffAll:
name: "Actions: Turn Off All"
collapsed: true
input:
action_turn_off_all:
name: Turn Off All Action ID
description: >
Action ID for all light turn off. Can be equal to any of turn on
actions ids, so the action will only trigger if at least one of
the provided lights is ON.
default: off_all
selector:
text:
action_turn_off_all_callback:
name: Callback
description: "Callback action triggered on common turn off event"
default: []
selector:
action: {}
Action_Group_TurnOffRepeat:
name: "Actions: Turn Off Repeat"
collapsed: true
input:
action_repeat_turn_off_callback:
name: Repeat Callback
description: >
Repeat callback action called if `Turn Off Action` arrives
when all the lights are already off.
default: []
selector:
action: {}
Action_Group_TurnOffAllRepeat:
name: "Actions: Turn Off All Repeat"
collapsed: true
input:
action_repeat_turn_off_all_callback:
name: Repeat Callback
description: >
Repeat callback action called if `Turn Off All Action` arrives
when all the lights are already off.
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Brightness Settings
# -------------------------------------------------------------------------
Group_Brightness:
name: "Brightness"
collapsed: true
input:
min_brightness:
name: Minimum Brightness
description: A number that specifies minimum brightness
default: 5
selector:
number:
min: 1
max: 255
step: 1
unit_of_measurement: "level"
max_brightness:
name: Maximum Brightness
description: A number that specifies maximum brightness
default: 255
selector:
number:
min: 1
max: 255
step: 1
unit_of_measurement: "level"
set_max_brightness_on_turn_on:
name: Set max brightness on turn on
description: "Check if max brightness should be set on the light turn on"
default: false
selector:
boolean:
transition_duration:
name: Transition Duration
description: >
Duration in seconds for light transitions (brightness, color changes).
Set to 0 for instant changes. Note: Hold actions ignore this setting
for immediate response.
default: 0
selector:
number:
min: 0
max: 10
step: 0.5
unit_of_measurement: "s"
# -------------------------------------------------------------------------
# Brightness Increment Actions
# -------------------------------------------------------------------------
Action_Group_Brightness_Increment:
name: "Actions: Brightness Increment"
collapsed: true
input:
action_brightness_decrease:
name: Brightness Decrease Action ID
description: Action ID for brightness decrease
default: brightness_step_down
selector:
text:
action_brightness_increase:
name: Brightness Increase Action ID
description: Action ID for brightness increase
default: brightness_step_up
selector:
text:
brightness_step_override:
name: Brightness Step Override
description: An override value that defines custom brightness step
default: 0
selector:
number:
min: 0
max: 255
step: 1
unit_of_measurement: "level"
action_brightness_increment_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Brightness Hold Actions (continuous adjustment while button held)
# -------------------------------------------------------------------------
Action_Group_Brightness_Hold:
name: "Actions: Brightness Hold"
collapsed: true
input:
action_brightness_hold:
name: Hold Action ID
description: Action ID for hold (start to change brightness)
default: hold
selector:
text:
action_brightness_release:
name: Release Action ID
description: Action ID for release (stop process of changing brightness after hold)
default: release
selector:
text:
hold_brightness_step:
name: Brightness Step
description: Amount to change brightness per tick (1-255)
default: 15
selector:
number:
min: 0
max: 255
step: 1
unit_of_measurement: "level"
# -------------------------------------------------------------------------
# Brightness List Actions (cycle through predefined values)
# -------------------------------------------------------------------------
Action_Group_Brightness_List:
name: "Actions: Brightness List"
collapsed: true
input:
action_brightness_list_up:
name: Brightness List Up Action ID
description: Action ID to set next value from brightness list
default: 'double'
selector:
text:
action_brightness_list_down:
name: Brightness List Down Action ID
description: Action ID to set previous value from brightness list
default: ''
selector:
text:
brightness_list_entity:
name: Brightness List Entity
description: An input_select entity containing brightness relative values (0, 25, 50 and etc.)
default: null
selector:
entity:
domain: input_select
action_brightness_list_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Brightness Min/Max Actions
# -------------------------------------------------------------------------
Action_Group_Brightness_MinMax:
name: "Actions: Brightness Set Min/Max"
collapsed: true
input:
action_brightness_set_min:
name: Set Min Brightness Action ID
description: Action ID to set minimum brightness value
default: 'set_min_brightness'
selector:
text:
action_brightness_set_min_callback:
name: Set Min Callback
description: "Set Min Callback Action"
default: []
selector:
action: {}
action_brightness_set_max:
name: Set Max Brightness Action ID
description: Action ID to set maximum brightness value
default: 'set_max_brightness'
selector:
text:
action_brightness_set_max_callback:
name: Set Max Callback
description: "Set Max Callback Action"
default: []
selector:
action: {}
action_brightness_set_min_or_max:
name: Set Min Or Max Brightness Action ID
description: >
Action ID to set min or max brightness value (decision will be made
based on current brightness value i.e. if current brightness is high,
then will set min brightness, the opposite logic for max brightness)
default: 'set_min_or_max_brightness'
selector:
text:
action_brightness_set_min_or_max_callback:
name: Set Min Or Max Callback
description: "Set Min Or Max Callback Action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Color Temperature Settings
# -------------------------------------------------------------------------
Action_Group_ColorTemp:
name: "Color Temperature"
collapsed: true
input:
min_color_temp:
name: Minimum Color Temperature
description: >
A number that specifies minimum color temperature.
Note: will be clamped to max temperature supported by the light.
default: 2000
selector:
number:
min: 2000
max: 12000
step: 100
unit_of_measurement: "K"
max_color_temp:
name: Maximum Color Temperature
description: >
A number that specifies maximum color temperature.
Note: will be clamped to max temperature supported by the light.
default: 7000
selector:
number:
min: 2000
max: 12000
step: 100
unit_of_measurement: "K"
# -------------------------------------------------------------------------
# Color Temperature Increment Actions
# -------------------------------------------------------------------------
Action_Group_ColorTemp_Increment:
name: "Actions: Color Temperature Increment"
collapsed: true
input:
action_color_temp_decrease:
name: Color Temperature Decrease Action ID
description: Action ID for color temperature decrease
default: color_temperature_step_down
selector:
text:
action_color_temp_increase:
name: Color Temperature Increase Action ID
description: Action ID for color temperature increase
default: color_temperature_step_up
selector:
text:
color_temp_step_override:
name: Color Temperature Step Override
description: An override value that overrides color temperature step size (in Kelvins)
default: 0
selector:
number:
min: 0
max: 2000
step: 100
unit_of_measurement: "K"
action_color_temp_increment_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Color Temperature Hold Actions
# -------------------------------------------------------------------------
Action_Group_ColorTemp_Hold:
name: "Actions: Color Temperature Hold"
collapsed: true
input:
action_color_temp_hold:
name: Hold Action ID
description: Action ID for hold (start to change color temperature)
default:
selector:
text:
action_color_temp_release:
name: Release Action ID
description: Action ID for release (stop process of changing color temperature after hold)
default:
selector:
text:
hold_color_temp_step:
name: Color Temperature Step
description: Amount to change color temperature per tick (0-1000)
default: 100
selector:
number:
min: 0
max: 1000
step: 50
unit_of_measurement: "K"
# -------------------------------------------------------------------------
# Color Temperature List Actions
# -------------------------------------------------------------------------
Action_Group_ColorTemp_List:
name: "Actions: Color Temperature List"
collapsed: true
input:
action_color_temp_list_up:
name: Color Temperature List Up Action ID
description: Action ID to set next value from color temperature list
default: ''
selector:
text:
action_color_temp_list_down:
name: Color Temperature List Down Action ID
description: Action ID to set previous value from color temperature list
default: ''
selector:
text:
color_temp_list_entity:
name: Color Temperature List Entity
description: An input_select entity containing Kelvin values (e.g., 2700, 4000, 6000)
default: null
selector:
entity:
domain: input_select
action_color_temp_list_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# RGB/Hue Settings
# -------------------------------------------------------------------------
Action_Group_RGB:
name: "RGB"
collapsed: true
input:
min_hue:
name: Minimum Hue
description: A number that specifies minimum hue value
default: 0
selector:
number:
min: 0
max: 360
step: 1
unit_of_measurement: "deg"
max_hue:
name: Maximum Hue
description: A number that specifies maximum hue value
default: 360
selector:
number:
min: 0
max: 360
step: 1
unit_of_measurement: "deg"
# -------------------------------------------------------------------------
# RGB Increment Actions
# -------------------------------------------------------------------------
Action_Group_RGB_Increment:
name: "Actions: RGB Increment"
collapsed: true
input:
action_hue_decrease:
name: Hue Decrease Action ID
description: Action ID for hue decrease
default: ''
selector:
text:
action_hue_increase:
name: Hue Increase Action ID
description: Action ID for hue increase
default: ''
selector:
text:
hue_step_override:
name: Hue Step Override
description: An override value that overrides hue step size
default: 0
selector:
number:
min: 0
max: 360
step: 1
unit_of_measurement: "deg"
action_hue_increment_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# RGB Hold Actions
# -------------------------------------------------------------------------
Action_Group_RGB_Hold:
name: "Actions: RGB Hold"
collapsed: true
input:
action_hue_hold:
name: Hold Action ID
description: Action ID for hold (start to change hue)
default:
selector:
text:
action_hue_release:
name: Release Action ID
description: Action ID for release (stop process of changing hue after hold)
default:
selector:
text:
hold_hue_step:
name: Hue Step
description: Amount to change hue per tick (0-360)
default: 30
selector:
number:
min: 0
max: 360
step: 1
unit_of_measurement: "deg"
# -------------------------------------------------------------------------
# RGB/Color List Actions
# -------------------------------------------------------------------------
Action_Group_Hue_List:
name: "Actions: RGB List"
collapsed: true
input:
action_color_list_up:
name: Color List Up Action ID
description: Action ID to set next value from color list
default: ''
selector:
text:
action_color_list_down:
name: Color List Down Action ID
description: Action ID to set previous value from color list
default: ''
selector:
text:
color_list_entity:
name: Color List Entity
description: An input_select entity containing color values. Hue values are currently supported.
default: null
selector:
entity:
domain: input_select
action_color_list_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Preset List Actions
# -------------------------------------------------------------------------
# Presets allow cycling through predefined light configurations
# Format: "key:value;key:value" (e.g., "color_temp_kelvin:4000;brightness:200")
Action_Group_Preset_List:
name: "Actions: Preset List"
collapsed: true
input:
action_preset_list_up:
name: Preset List Up Action ID
description: Action ID to set next value from preset list
default: ''
selector:
text:
action_preset_list_down:
name: Preset List Down Action ID
description: Action ID to set previous value from preset list
default: ''
selector:
text:
preset_list_entity:
name: Preset List Entity
description: >
An input_select entity containing preset values in JSON format.
Example: `{"color_temp_kelvin": 4000, "brightness": 255}`
Example with RGB: `{"rgb_color": [255, 0, 0], "brightness": 200}`
Example with effect: `{"effect": "colorloop", "brightness": 128}`
Supported keys: brightness, color_temp_kelvin, rgb_color, hs_color,
xy_color, effect, transition
default: null
selector:
entity:
domain: input_select
action_preset_list_callback:
name: Callback
description: "Callback action"
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Scene Activation Actions
# -------------------------------------------------------------------------
# Directly activate Home Assistant scenes via MQTT actions
Action_Group_Scene:
name: "Actions: Scene Activation"
collapsed: true
input:
action_scene_1:
name: Scene 1 Action ID
description: Action ID to activate Scene 1
default: ''
selector:
text:
scene_entity_1:
name: Scene 1 Entity
description: The scene entity to activate when Scene 1 Action ID is received
default: null
selector:
entity:
domain: scene
action_scene_1_callback:
name: Scene 1 Callback
description: Callback action triggered after Scene 1 activation
default: []
selector:
action: {}
action_scene_2:
name: Scene 2 Action ID
description: Action ID to activate Scene 2
default: ''
selector:
text:
scene_entity_2:
name: Scene 2 Entity
description: The scene entity to activate when Scene 2 Action ID is received
default: null
selector:
entity:
domain: scene
action_scene_2_callback:
name: Scene 2 Callback
description: Callback action triggered after Scene 2 activation
default: []
selector:
action: {}
action_scene_3:
name: Scene 3 Action ID
description: Action ID to activate Scene 3
default: ''
selector:
text:
scene_entity_3:
name: Scene 3 Entity
description: The scene entity to activate when Scene 3 Action ID is received
default: null
selector:
entity:
domain: scene
action_scene_3_callback:
name: Scene 3 Callback
description: Callback action triggered after Scene 3 activation
default: []
selector:
action: {}
action_scene_4:
name: Scene 4 Action ID
description: Action ID to activate Scene 4
default: ''
selector:
text:
scene_entity_4:
name: Scene 4 Entity
description: The scene entity to activate when Scene 4 Action ID is received
default: null
selector:
entity:
domain: scene
action_scene_4_callback:
name: Scene 4 Callback
description: Callback action triggered after Scene 4 activation
default: []
selector:
action: {}
# -------------------------------------------------------------------------
# Common List Parameters
# -------------------------------------------------------------------------
CommonListParameter:
name: "Common List Parameters"
collapsed: true
input:
list_control_loop:
name: List Control Loop
description: >
Controls if increment should restart the loop when reaching end index
i.e. reaching border values will reset current index. It makes sense
to turn off the option if you specify separate actions for list up/list down actions.
default: true
selector:
boolean:
# -------------------------------------------------------------------------
# Common Hold Parameters
# -------------------------------------------------------------------------
CommonHoldParameter:
name: "Common Hold Parameters"
collapsed: true
input:
hold_delay_ms:
name: Delay Between Hold Action Steps (ms)
description: Delay between brightness changes during hold
default: 100
selector:
number:
min: 16
max: 2000
step: 20
unit_of_measurement: "ms"
# -------------------------------------------------------------------------
# Global Actions/Conditions
# -------------------------------------------------------------------------
ActionsGroup:
name: "Actions"
collapsed: true
input:
any_action_callback:
name: Any Action Callback
description: Action(s) to run when any action triggered
default: []
selector:
action: {}
automation_condition:
name: Condition Action
description: Automation will only run if the condition statement is true
default: []
selector:
condition: {}
# -------------------------------------------------------------------------
# Custom Actions (up to 4 custom action handlers)
# -------------------------------------------------------------------------
CustomActionsGroup:
name: "Custom Actions"
collapsed: true
input:
action_custom_1:
name: Action ID 1
description: Action ID that trigger callback 1
default: ""
selector:
text: {}
action_custom_callback_1:
name: Action Callback 1
description: Actions to run when Action ID 1 is received
default: []
selector:
action: {}
action_custom_2:
name: Action ID 2
description: Action ID that trigger callback 2
default: ""
selector:
text: {}
action_custom_callback_2:
name: Action Callback 2
description: Actions to run when Action ID 2 is received
default: []
selector:
action: {}
action_custom_3:
name: Action ID 3
description: Action ID that trigger callback 3
default: ""
selector:
text: {}
action_custom_callback_3:
name: Action Callback 3
description: Actions to run when Action ID 3 is received
default: []
selector:
action: {}
action_custom_4:
name: Action ID 4
description: Action ID that trigger callback 4
default: ""
selector:
text: {}
action_custom_callback_4:
name: Action Callback 4
description: Actions to run when Action ID 4 is received
default: []
selector:
action: {}
# =============================================================================
# AUTOMATION CONDITIONS
# =============================================================================
# Only proceed if:
# 1. The MQTT payload contains an 'action' field
# 2. User-defined conditions are met
condition:
- condition: template
value_template: "{{ 'action' in trigger.payload_json }}"
- condition: !input automation_condition
# =============================================================================
# MQTT TRIGGERS
# =============================================================================
# Listen to all configured MQTT topics
trigger:
- platform: mqtt
topic: !input mqtt_topic
- platform: mqtt
topic: !input mqtt_topic2
- platform: mqtt
topic: !input mqtt_topic3
- platform: mqtt
topic: !input mqtt_topic4
# =============================================================================
# VARIABLES
# =============================================================================
variables:
# ---------------------------------------------------------------------------
# Persistent State Keys (JSON structure keys)
# ---------------------------------------------------------------------------
# Keys used to store/retrieve values from the persistent state JSON
state_key_hold_direction: 'hd' # Stores hold direction (1 or -1)
state_key_last_action_step_size: 'lass' # Stores last action step size
state_key_preset_index: 'pi' # Stores current preset list index
# Prefixes for color mode-specific state keys
state_key_color_temp_mode_prefix: 'ctm_' # Color temperature mode prefix
state_key_hue_mode_prefix: 'hm_' # Hue/RGB mode prefix
# Postfixes for state keys
state_postfix_last_list_index: 'lli' # Last list index postfix
state_postfix_brightness: 'br' # Brightness postfix
# ---------------------------------------------------------------------------
# Action IDs from Input
# ---------------------------------------------------------------------------
# Turn ON action identifiers
action_turn_on: !input action_turn_on
action_turn_on_all: !input action_turn_on_all
# Turn OFF action identifiers
action_turn_off: !input action_turn_off
action_turn_off_all: !input action_turn_off_all
# ---------------------------------------------------------------------------
# Alternative Light Configuration
# ---------------------------------------------------------------------------
alternative_light_1: !input alternative_light_1
alternative_light_2: !input alternative_light_2
alternative_light_1_toggle_callback: !input alternative_light_1_toggle_callback
alternative_light_2_toggle_callback: !input alternative_light_2_toggle_callback
# Check if alternative action IDs are properly configured
# (turn on/off actions must be different for alternative lights to work)
is_alternative_action_id_supported: >
{{ action_turn_on != action_turn_off
and (action_turn_on_all != action_turn_off_all) if (action_turn_off_all != '' and action_turn_on_all != '') else true }}
# Alternative lights are supported if at least one is configured and action IDs are valid
is_alternative_light_supported: >
{{ (alternative_light_1 is not none or alternative_light_2 is not none)
and is_alternative_action_id_supported }}
# ---------------------------------------------------------------------------
# Target Light Resolution
# ---------------------------------------------------------------------------
apply_common_options_to_newly_enabled_light: !input apply_common_options_to_newly_enabled_light
target_lights_helper: !input target_lights_helper
target_lights: !input target_lights
target_light_group: !input target_light_group
target_area: !input target_area
best_source_light_for_options: !input best_source_light_for_options
# Resolve all lights from direct selection, helpers, groups, and areas
resolved_all_lights: >-
{% set result = [] %}
{# Direct light selection #}
{% if target_lights | length > 0 %}
{% set result = result + target_lights %}
{% endif %}
{# Helper-based selection #}
{% if target_lights_helper != '' %}
{% set result = result + (target_lights_helper | map('states') | list) %}
{% endif %}
{# Light group - add the group entity itself #}
{% if target_light_group is not none %}
{% set result = result + [target_light_group] %}
{% endif %}
{# Area-based selection #}
{% if target_area != '' %}
{% set area_lights = area_entities(target_area) | select('match', '^light\\.') | list %}
{% set result = result + area_lights %}
{% endif %}
{# Remove duplicates while preserving order #}
{% set seen = namespace(items=[]) %}
{% for item in result %}
{% if item not in seen.items %}
{% set seen.items = seen.items + [item] %}
{% endif %}
{% endfor %}
{{ seen.items }}
# Determine which lights are currently enabled (ON state)
enabled_lights_default: "{{ resolved_all_lights | select('is_state','on') | list }}"
are_all_lights_on_default: "{{ enabled_lights_default | length == resolved_all_lights | length }}"
are_all_lights_off_default: "{{ enabled_lights_default | length == 0 }}"
is_any_light_on_default: "{{ enabled_lights_default | length != 0 }}"
# Find the first disabled light (for sequential turn on)
first_not_enabled_from_start_index: >
{% set ns = namespace(idx=-1) %}
{% for i in range(resolved_all_lights|length) %}
{% if not is_state(resolved_all_lights[i], 'on') %}
{% set ns.idx = i %}
{% break %}
{% endif %}
{% endfor %}
{{ ns.idx }}
# Find the last enabled light (for sequential turn off)
first_enabled_index_from_end: >
{% set ns = namespace(idx=-1) %}
{% for i in range(resolved_all_lights|length - 1, -1, -1) %}
{% if is_state(resolved_all_lights[i], 'on') %}
{% set ns.idx = i %}
{% break %}
{% endif %}
{% endfor %}
{{ ns.idx }}
# Determine which devices to control (primary lights or alternative if all primary are off)
devices_to_control: >
{% if are_all_lights_off_default and is_alternative_light_supported %}
{% set items = [] %}
{% if alternative_light_1 is not none and is_state(alternative_light_1, 'on') %}
{% set items = items + [alternative_light_1] %}
{% endif %}
{% if alternative_light_2 is not none and is_state(alternative_light_2, 'on') %}
{% set items = items + [alternative_light_2] %}
{% endif %}
{{ items }}
{% else %}
{{ enabled_lights_default }}
{% endif %}
# Filter to only light entities (exclude switches for brightness/color operations)
lights_to_control: >
{{ devices_to_control | list | select('match', '^light\\.') | list }}
# Determine the reference light for reading current options (brightness, color, etc.)
options_reference_light: >
{% if (best_source_light_for_options is not none) and is_state(best_source_light_for_options, 'on') %}
{{ best_source_light_for_options }}
{% else %}
{{ none if (lights_to_control | length == 0) else lights_to_control[(lights_to_control | length) - 1] }}
{% endif %}
enabled_lights: "{{ lights_to_control | select('is_state','on') | list }}"
is_any_light_on: "{{ lights_to_control | length != 0 }}"
# ---------------------------------------------------------------------------
# Persistent State Management
# ---------------------------------------------------------------------------
automation_state_entity: !input automation_state_entity
# Parse the global state JSON from the input_text entity
automation_state_global: >
{% if automation_state_entity is not none %}
{% set text = states(automation_state_entity) | string %}
{% if text in ['unknown','unavailable','none',''] %}
{{ dict() }}
{% else %}
{{ text | from_json }}
{% endif %}
{% else %}
{{ dict() }}
{% endif %}
automation_state_key_override: !input automation_state_key_override
default_automation_state_key: 'mqtt_light_control'
# Determine the key for this automation's state within the global state
automation_state_key: >
{% if automation_state_key_override != '' %}
{{ automation_state_key_override }}
{% else %}
{{ resolved_all_lights[0] if resolved_all_lights | length > 0 else default_automation_state_key }}
{% endif %}
# Get this automation's state from the global state
automation_state: "{{ automation_state_global.get(automation_state_key, dict()) }}"
# ---------------------------------------------------------------------------
# Action Step Resolution
# ---------------------------------------------------------------------------
action_step_payload_key: !input action_step_payload_key
action_step_entity: !input action_step_entity
action_step_fallback: !input action_step_fallback
# Extract the action ID from the MQTT payload
action_id: "{{ trigger.payload_json.action }}"
# Resolve action step size from payload, entity, or fallback
action_step_size: >-
{% set key = action_step_payload_key %}
{% if key != '' and key in trigger.payload_json %}
{{ trigger.payload_json[key] }}
{% elif action_step_entity is not none %}
{% set value = states(action_step_entity) %}
{{ 0 if value in ['unknown', 'unavailable', 'none', ''] else value | int }}
{% else %}
{{ action_step_fallback }}
{% endif %}
# Use stored step size if current is zero (for devices that don't report step on release)
result_action_step_size: >
{{ (automation_state.get(state_key_last_action_step_size,0) | int) if action_step_size == 0 else action_step_size }}
# ---------------------------------------------------------------------------
# Brightness Action IDs
# ---------------------------------------------------------------------------
action_brightness_list_up: !input action_brightness_list_up
action_brightness_list_down: !input action_brightness_list_down
action_brightness_decrease: !input action_brightness_decrease
action_brightness_increase: !input action_brightness_increase
action_brightness_hold: !input action_brightness_hold
action_brightness_release: !input action_brightness_release
action_brightness_set_min: !input action_brightness_set_min
action_brightness_set_max: !input action_brightness_set_max
action_brightness_set_min_or_max: !input action_brightness_set_min_or_max
result_max_brightness: !input max_brightness
result_min_brightness: !input min_brightness
transition_duration: !input transition_duration
# ---------------------------------------------------------------------------
# Color Temperature Action IDs and Limits
# ---------------------------------------------------------------------------
action_color_temp_list_up: !input action_color_temp_list_up
action_color_temp_list_down: !input action_color_temp_list_down
action_color_temp_decrease: !input action_color_temp_decrease
action_color_temp_increase: !input action_color_temp_increase
action_color_temp_hold: !input action_color_temp_hold
action_color_temp_release: !input action_color_temp_release
min_color_temp: !input min_color_temp
max_color_temp: !input max_color_temp
# Calculate effective min/max color temp based on light capabilities
# Clamps user-defined range to what lights actually support
min_max_mireds_values: >
{% set ns = namespace(min=min_color_temp,max=max_color_temp) %}
{% for l in resolved_all_lights %}
{% set light_min = state_attr(l, 'min_color_temp_kelvin') %}
{% if light_min is not none %}
{% set ns.min = [ns.min, light_min | int] | max %}
{% endif %}
{% set light_max = state_attr(l, 'max_color_temp_kelvin') %}
{% if light_max is not none %}
{% set ns.max = [ns.max, light_max | int] | min %}
{% endif %}
{% endfor %}
{{ [ns.min, ns.max] }}
result_min_color_temp: "{{ min_max_mireds_values[0] }}"
result_max_color_temp: "{{ min_max_mireds_values[1] }}"
# ---------------------------------------------------------------------------
# Hue/RGB Action IDs and Limits
# ---------------------------------------------------------------------------
action_color_list_up: !input action_color_list_up
action_color_list_down: !input action_color_list_down
action_hue_decrease: !input action_hue_decrease
action_hue_increase: !input action_hue_increase
action_hue_hold: !input action_hue_hold
action_hue_release: !input action_hue_release
result_max_hue: !input max_hue
result_min_hue: !input min_hue
default_saturation: 100
rgb_color_modes: ['rgb_color', 'hs_color', 'hs', 'xy']
# ---------------------------------------------------------------------------
# Preset Action IDs
# ---------------------------------------------------------------------------
action_preset_list_up: !input action_preset_list_up
action_preset_list_down: !input action_preset_list_down
# ---------------------------------------------------------------------------
# Scene Action IDs and Entities
# ---------------------------------------------------------------------------
action_scene_1: !input action_scene_1
action_scene_2: !input action_scene_2
action_scene_3: !input action_scene_3
action_scene_4: !input action_scene_4
scene_entity_1: !input scene_entity_1
scene_entity_2: !input scene_entity_2
scene_entity_3: !input scene_entity_3
scene_entity_4: !input scene_entity_4
# ---------------------------------------------------------------------------
# Callbacks
# ---------------------------------------------------------------------------
any_action_callback: !input any_action_callback
# ---------------------------------------------------------------------------
# Action ID Groups (for condition matching)
# ---------------------------------------------------------------------------
turn_on_action_ids: "{{ [action_turn_on, action_turn_on_all] }}"
turn_off_action_ids: "{{ [action_turn_off, action_turn_off_all] }}"
turn_on_and_off_action_ids: "{{ turn_on_action_ids + turn_off_action_ids }}"
# Combined release action for all hold types (brightness, color temp, hue)
action_release: >
{% set releases = [action_brightness_release, action_color_temp_release, action_hue_release] %}
{% for r in releases %}
{% if r is not none and r != '' and action_id == r %}
{{ r }}
{% endif %}
{% endfor %}
# ---------------------------------------------------------------------------
# Custom Action IDs
# ---------------------------------------------------------------------------
action_custom_1: !input action_custom_1
action_custom_2: !input action_custom_2
action_custom_3: !input action_custom_3
action_custom_4: !input action_custom_4
# ---------------------------------------------------------------------------
# Current Light State
# ---------------------------------------------------------------------------
now_color_mode: "{{ state_attr(options_reference_light, 'color_mode') if options_reference_light else '' }}"
now_is_color_temp_mode: "{{ now_color_mode in ['color_temp'] }}"
now_is_rgb_mode: "{{ now_color_mode in rgb_color_modes }}"
# Determine state key prefix based on current color mode
now_mode_state_prefix: >
{% if now_is_color_temp_mode %}
{{ state_key_color_temp_mode_prefix }}
{% elif now_is_rgb_mode %}
{{ state_key_hue_mode_prefix }}
{% else %}
{{ '' }}
{% endif %}
now_mode_state_brightness: "{{ now_mode_state_prefix + 'last_brightness' }}"
# ---------------------------------------------------------------------------
# Debug Flags
# ---------------------------------------------------------------------------
is_debug: false # Detailed debug for specific actions
is_base_debug: false # Basic debug info at start
# =============================================================================
# AUTOMATION MODE
# =============================================================================
# Restart mode ensures hold actions can be interrupted by release
mode: restart
# =============================================================================
# ACTION SEQUENCE
# =============================================================================
action:
# ---------------------------------------------------------------------------
# DEBUG: Log basic info if enabled
# ---------------------------------------------------------------------------
- choose:
- conditions:
- condition: template
value_template: "{{ is_base_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info"
message: >
action_id = {{ action_id }},
lights_to_control = {{ lights_to_control }},
options_reference_light = {{ options_reference_light }},
action_brightness_set_min_or_max = {{ action_brightness_set_min_or_max }},
is_any_light_on = {{ is_any_light_on }}
# ---------------------------------------------------------------------------
# GUARDS: Validate prerequisites before processing
# ---------------------------------------------------------------------------
- choose:
# Guard: No lights resolved - cannot proceed
- conditions:
- condition: template
value_template: "{{ resolved_all_lights | length == 0 }}"
sequence:
- stop: "No light target defined. Please set either 'Target Light' or 'Target Light Helper'."
# Guard: Empty action ID - nothing to do
- conditions:
- condition: template
value_template: "{{ action_id == '' }}"
sequence:
- stop: "MQTT action ID is empty. Stopping execution."
# ===========================================================================
# MAIN ACTION ROUTER
# ===========================================================================
- choose:
# -----------------------------------------------------------------------
# TURN ON / TURN OFF ACTIONS
# -----------------------------------------------------------------------
# Handles: action_turn_on, action_turn_on_all, action_turn_off, action_turn_off_all
- conditions:
- condition: template
value_template: >
{{ action_id in turn_on_and_off_action_ids }}
sequence:
- variables:
# Check if repeat actions are supported (requires distinct on/off action IDs)
repeat_supported: >
{{ not (action_turn_on in turn_off_action_ids)
and not (action_turn_on_all in turn_off_action_ids)
and not (action_turn_off in turn_on_action_ids)
and not (action_turn_off_all in turn_on_action_ids) }}
# Identify which action was triggered
is_action_turn_on: "{{ action_id == action_turn_on }}"
is_action_turn_on_all: "{{ action_id == action_turn_on_all }}"
is_action_turn_off: "{{ action_id == action_turn_off }}"
is_action_turn_off_all: "{{ action_id == action_turn_off_all }}"
# Determine if this should be a turn on action
# (considers shared on/off action IDs - only turn on if lights are off)
is_turn_on_default: >
{% if is_action_turn_on %}
{{ not is_any_light_on_default if action_turn_on == action_turn_off else true }}
{% elif is_action_turn_on_all %}
{{ not is_any_light_on_default if action_turn_on_all == action_turn_off_all else true }}
{% else %}
{{ false }}
{% endif %}
is_turn_off_default: "{{ not is_turn_on_default }}"
# Check if we should target an alternative light instead
alternative_target: >
{% if not is_alternative_light_supported or is_any_light_on_default or is_turn_on_default %}
{{ none }}
{% elif alternative_light_1 is not none and action_id == action_turn_off %}
{{ alternative_light_1 }}
{% elif alternative_light_2 is not none and action_id == action_turn_off_all %}
{{ alternative_light_2 }}
{% else %}
{{ none }}
{% endif %}
# Determine if we're turning on/off an alternative light
is_turn_on_alternative: >
{% if alternative_target is not none %}
{{ is_state(alternative_target, 'off') }}
{% else %}
{{ false }}
{% endif %}
is_turn_off_alternative: >
{% if alternative_target is not none %}
{{ is_state(alternative_target, 'on') }}
{% else %}
{{ false }}
{% endif %}
# Final determination of action type
is_turn_on: "{{ is_turn_on_default or is_turn_on_alternative }}"
is_turn_off: "{{ is_turn_off_default or is_turn_off_alternative }}"
# Check if alternative light callback should be triggered
is_alternative_callback: >
{{ not is_any_light_on_default
and ((action_id == action_turn_off and alternative_light_1_toggle_callback != []) or (action_id == action_turn_off_all and alternative_light_2_toggle_callback != [])) }}
# Debug logging for turn on/off
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Turn On/Off)"
message: >
action_id = {{ action_id }},
is_turn_on = {{ is_turn_on }},
alternative_target = {{ alternative_target }},
is_alternative_callback = {{ is_alternative_callback }}
# Handle alternative light toggle callbacks
- choose:
- conditions:
- condition: template
value_template: "{{ is_alternative_callback }}"
sequence:
- choose:
# Alternative 1 Toggle Callback
- conditions:
- condition: template
value_template: "{{ action_id == action_turn_off and alternative_light_1_toggle_callback != [] }}"
sequence: !input alternative_light_1_toggle_callback
# Alternative 2 Toggle Callback
- conditions:
- condition: template
value_template: "{{ action_id == action_turn_off_all and alternative_light_2_toggle_callback != [] }}"
sequence: !input alternative_light_2_toggle_callback
# Main turn on/off logic
- choose:
# ----- TURN ON -----
- conditions:
- condition: template
value_template: "{{ is_turn_on }}"
sequence:
- variables:
is_repeated: "{{ are_all_lights_on_default and alternative_target is none }}"
is_turn_on_all: "{{ action_id == action_turn_on_all and alternative_target is none }}"
- choose:
# Non-repeated turn on (actually turn on lights)
- conditions:
- condition: template
value_template: "{{ not is_repeated }}"
sequence:
- variables:
action_turn_on_callback: !input action_turn_on_callback
action_turn_on_all_callback: !input action_turn_on_all_callback
set_max_brightness_on_turn_on: !input set_max_brightness_on_turn_on
# Determine which devices to turn on
devices_to_turn_on: >
{% if alternative_target is not none %}
{{ [alternative_target] }}
{% else %}
{% if is_turn_on_all %}
{{ resolved_all_lights }}
{% else %}
{{ resolved_all_lights[:first_not_enabled_from_start_index + 1] if first_not_enabled_from_start_index != -1 else [] }}
{% endif %}
{% endif %}
# Separate lights and switches (different service calls)
lights_to_turn_on: >
{{ (devices_to_turn_on | list) | select('match', '^light\\.') | list }}
switches_to_turn_on: >
{{ (devices_to_turn_on | list) | select('match', '^switch\\.') | list }}
# Determine reference light for copying settings
result_options_reference_light: "{{ alternative_target if alternative_target is not none else options_reference_light }}"
color_mode: "{{ state_attr(result_options_reference_light, 'color_mode') if (result_options_reference_light is not none) else none }}"
# Build light data (brightness, color, transition) to apply
light_data: >
{% set d = dict() %}
{% if transition_duration > 0 %}
{% set d = d | combine({ 'transition': transition_duration }) %}
{% endif %}
{% if not color_mode or (result_options_reference_light is none) or (not apply_common_options_to_newly_enabled_light) %}
{% if set_max_brightness_on_turn_on %}
{% set d = d | combine({ 'brightness': result_max_brightness }) %}
{% endif %}
{{ d }}
{% else %}
{% set brightness = 0 %}
{% if set_max_brightness_on_turn_on %}
{% set brightness = result_max_brightness %}
{% else %}
{% set brightness = state_attr(result_options_reference_light, 'brightness') | int %}
{% endif %}
{% set d = d | combine({ 'brightness': brightness }) %}
{% if color_mode in ['brightness','color_temp'] %}
{% set color_temp = state_attr(result_options_reference_light, 'color_temp_kelvin') %}
{% if color_temp is not none %}
{% set d = d | combine({ 'color_temp_kelvin': color_temp | int }) %}
{% endif %}
{% else %}
{% set rgb_color = state_attr(result_options_reference_light, 'rgb_color') %}
{% if rgb_color is not none %}
{% set d = d | combine({ 'rgb_color': rgb_color }) %}
{% endif %}
{% endif %}
{{ d }}
{% endif %}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Turn ON)"
message: >
lights_to_turn_on = {{ lights_to_turn_on }},
switches_to_turn_on = {{ switches_to_turn_on }}
# Actually turn ON the lights
- service: light.turn_on
target:
entity_id: "{{ lights_to_turn_on }}"
data: "{{ light_data }}"
# Actually turn ON the switches
- service: switch.turn_on
target:
entity_id: "{{ switches_to_turn_on }}"
# Execute turn on all callback
- choose:
- conditions:
- condition: template
value_template: "{{ is_turn_on_all and action_turn_on_all_callback != [] }}"
sequence: !input action_turn_on_all_callback
# Execute turn on callback
- choose:
- conditions:
- condition: template
value_template: "{{ (not is_turn_on_all) and action_turn_on_callback != [] }}"
sequence: !input action_turn_on_callback
# Repeated turn on (all lights already on)
- conditions:
- condition: template
value_template: "{{ is_repeated }}"
sequence:
- variables:
action_repeat_turn_on_callback: !input action_repeat_turn_on_callback
action_repeat_turn_on_all_callback: !input action_repeat_turn_on_all_callback
action_for_repeated_turn_on: !input action_for_repeated_turn_on
action_for_repeated_turn_on_all: !input action_for_repeated_turn_on_all
result_action: "{{ action_for_repeated_turn_on_all if is_turn_on_all else action_for_repeated_turn_on }}"
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Turn ON Repeated)"
message: >
result_action = {{ result_action }}
# Publish repeat action to MQTT if configured
- choose:
- conditions:
- condition: template
value_template: "{{ result_action != '' }}"
sequence:
- variables:
message_payload: >
{% set new_msg = trigger.payload_json | combine({'action': result_action}) %}
{{ new_msg | tojson }}
- service: mqtt.publish
data:
topic: "{{ trigger.topic }}"
payload: "{{ message_payload }}"
# Execute repeat callbacks
- choose:
- conditions:
- condition: template
value_template: "{{ is_turn_on_all and action_repeat_turn_on_all_callback != [] }}"
sequence: !input action_repeat_turn_on_all_callback
- choose:
- conditions:
- condition: template
value_template: "{{ (not is_turn_on_all) and action_repeat_turn_on_callback != [] }}"
sequence: !input action_repeat_turn_on_callback
# ----- TURN OFF -----
- conditions:
- condition: template
value_template: "{{ is_turn_off }}"
sequence:
- variables:
is_repeated: "{{ are_all_lights_off_default and alternative_target is none }}"
is_turn_off_all: "{{ action_id == action_turn_off_all and alternative_target is none }}"
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Turn OFF)"
message: >
action_id = {{ action_id }},
are_all_lights_off_default = {{ are_all_lights_off_default }}
- choose:
# Non-repeated turn off (actually turn off lights)
- conditions:
- condition: template
value_template: "{{ not is_repeated }}"
sequence:
- variables:
# Determine which devices to turn off
devices_to_turn_off: >
{% if alternative_target is not none %}
{{ [alternative_target] }}
{% else %}
{% if is_turn_off_all %}
{{ resolved_all_lights }}
{% else %}
{{ [resolved_all_lights[first_enabled_index_from_end]] }}
{% endif %}
{% endif %}
lights_to_turn_off: >
{{ (devices_to_turn_off | list) | select('match', '^light\\.') | list }}
switches_to_turn_off: >
{{ (devices_to_turn_off | list) | select('match', '^switch\\.') | list }}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Turn OFF Non Repeated)"
message: >
devices_to_turn_off = {{ devices_to_turn_off }},
lights_to_turn_off = {{ lights_to_turn_off }},
switches_to_turn_off = {{ switches_to_turn_off }}
# Turn off the lights
- service: light.turn_off
target:
entity_id: "{{ lights_to_turn_off }}"
data:
transition: "{{ transition_duration }}"
# Turn off the switches
- service: switch.turn_off
target:
entity_id: "{{ switches_to_turn_off }}"
# Execute turn off all callback
- choose:
- conditions:
- condition: template
value_template: "{{ is_turn_off_all and action_turn_off_all_callback != [] }}"
sequence: !input action_turn_off_all_callback
# Execute turn off callback
- choose:
- conditions:
- condition: template
value_template: "{{ (not is_turn_off_all) and action_turn_off_callback != [] }}"
sequence: !input action_turn_off_callback
# Repeated turn off (all lights already off)
- conditions:
- condition: template
value_template: "{{ is_repeated }}"
sequence:
# Execute repeat callbacks
- choose:
- conditions:
- condition: template
value_template: "{{ is_turn_off_all and action_repeat_turn_off_all_callback != [] }}"
sequence: !input action_repeat_turn_off_all_callback
- choose:
- conditions:
- condition: template
value_template: "{{ (not is_turn_off_all) and action_repeat_turn_off_callback != [] }}"
sequence: !input action_repeat_turn_off_callback
# Execute any_action_callback for turn on/off
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# -----------------------------------------------------------------------
# BRIGHTNESS SET MIN OR MAX (toggle between min/max based on current)
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ action_id in [action_brightness_set_min_or_max]
and is_any_light_on }}
sequence:
- variables:
current_brightness: "{{ state_attr(options_reference_light, 'brightness') | int }}"
threshold: "{{ ((result_max_brightness + result_min_brightness) / 2) | int }}"
should_set_max: "{{ current_brightness < threshold }}"
result_brightness: "{{ result_max_brightness if should_set_max else result_min_brightness }}"
action_brightness_set_min_or_max_callback: !input action_brightness_set_min_or_max_callback
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Set Min Or Max Brightness)"
message: >
should_set_max = {{ should_set_max }},
result_brightness = {{ result_brightness }}
# Apply brightness
- service: light.turn_on
target:
entity_id: "{{ lights_to_control }}"
data:
brightness: "{{ result_brightness }}"
transition: "{{ transition_duration }}"
- choose:
- conditions:
- condition: template
value_template: "{{ action_brightness_set_min_or_max_callback != [] }}"
sequence: !input action_brightness_set_min_or_max_callback
# -----------------------------------------------------------------------
# BRIGHTNESS SET MAX / SET MIN (explicit min or max)
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ action_id in [action_brightness_set_max, action_brightness_set_min]
and is_any_light_on }}
sequence:
- variables:
should_set_max: "{{ action_id == action_brightness_set_max }}"
result_brightness: "{{ result_max_brightness if should_set_max else result_min_brightness }}"
action_brightness_set_min_callback: !input action_brightness_set_min_callback
action_brightness_set_max_callback: !input action_brightness_set_max_callback
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Set Min/Max Brightness)"
message: >
should_set_max = {{ should_set_max }},
result_brightness = {{ result_brightness }}
# Apply brightness
- service: light.turn_on
target:
entity_id: "{{ lights_to_control }}"
data:
brightness: "{{ result_brightness }}"
transition: "{{ transition_duration }}"
- choose:
- conditions:
- condition: template
value_template: "{{ (not should_set_max) and action_brightness_set_min_callback != [] }}"
sequence: !input action_brightness_set_min_callback
- choose:
- conditions:
- condition: template
value_template: "{{ should_set_max and action_brightness_set_max_callback != [] }}"
sequence: !input action_brightness_set_max_callback
# -----------------------------------------------------------------------
# HOLD ACTIONS (brightness, color temp, or hue)
# -----------------------------------------------------------------------
# Continuously adjusts value while hold action is active
# Stops when release action is received (mode: restart handles this)
- conditions:
- condition: template
value_template: >
{{ action_id in [action_brightness_hold, action_color_temp_hold, action_hue_hold]
and is_any_light_on
and automation_state_entity != '' }}
sequence:
- variables:
is_brightness: "{{ action_id in [action_brightness_hold] }}"
is_color_temp: "{{ action_id in [action_color_temp_hold] }}"
is_hue: "{{ action_id in [action_hue_hold] }}"
direction_invert_threshold: 0.05
hold_brightness_step: !input hold_brightness_step
hold_color_temp_step: !input hold_color_temp_step
hold_hue_step: !input hold_hue_step
# Build hold state array: [min, max, step, attribute_name]
hold_state: >
{% if is_brightness %}
{{ [result_min_brightness, result_max_brightness, hold_brightness_step | int, 'brightness'] }}
{% elif is_color_temp %}
{{ [result_min_color_temp, result_max_color_temp, hold_color_temp_step | int, 'color_temp_kelvin'] }}
{% elif is_hue %}
{{ [result_min_hue, result_max_hue, hold_hue_step | int, 'hs_color'] }}
{% else %}
{{ [] }}
{% endif %}
hold_delay_ms: !input hold_delay_ms
hold_direction_from_state: "{{ automation_state.get(state_key_hold_direction, 1) | int }}"
min_value: "{{ hold_state[0] | int }}"
max_value: "{{ hold_state[1] | int }}"
step_value: "{{ hold_state[2] | int }}"
result_attribute: "{{ hold_state[3] }}"
# Get initial value
initial_value: >
{% set v = state_attr(options_reference_light, result_attribute) %}
{% if v is none %}
{{ 0 }}
{% elif is_hue %}
{{ v[0] }}
{% else %}
{{ v }}
{% endif %}
# Determine hold direction (invert if near min/max boundaries)
hold_direction: >
{% set size = max_value - min_value %}
{% set threshold = ((size | float) * direction_invert_threshold) | int %}
{% set diff_to_max = max_value - initial_value %}
{% set diff_to_min = initial_value - min_value %}
{% if diff_to_max < threshold %}
{{ -1 }}
{% elif diff_to_min < threshold %}
{{ 1 }}
{% else %}
{{ 1 if hold_direction_from_state == 0 else -1 }}
{% endif %}
signed_step_value: "{{ step_value if (hold_direction == 1) else -step_value }}"
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Hold Start)"
message: >
options_reference_light = {{ options_reference_light }},
hold_state = {{ hold_state }},
action_id = {{ action_id }},
hold_direction = {{ hold_direction }},
signed_step_value = {{ signed_step_value }},
result_attribute = {{ result_attribute }}
# Save hold direction to persistent state
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({ state_key_hold_direction: hold_direction })) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# Execute any_action_callback
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# Hold loop - continues until automation is restarted by release action
- repeat:
while:
- condition: template
value_template: "true"
sequence:
- variables:
current_value: >
{% set v = state_attr(options_reference_light, result_attribute) %}
{% if v is none %}
{{ 0 }}
{% elif is_hue %}
{{ v[0] | int }}
{% else %}
{{ v | int }}
{% endif %}
next_value: "{{ (current_value + signed_step_value) | int }}"
next_value_clamped: "{{ [min_value, [max_value, next_value]|min]|max }}"
value_to_set: >
{% if is_hue %}
{{ [next_value_clamped, default_saturation] }}
{% else %}
{{ next_value_clamped }}
{% endif %}
light_data: >
{% set d = dict() %}
{% set d = d | combine({ result_attribute: value_to_set }) %}
{{ d }}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Hold In Progress)"
message: >
signed_step_value = {{ signed_step_value }},
current_value = {{ current_value }},
value_to_set = {{ value_to_set }}
# Apply value to lights
- service: light.turn_on
target:
entity_id: "{{ lights_to_control }}"
data: "{{ light_data }}"
# Wait before next iteration
- delay:
milliseconds: "{{ hold_delay_ms }}"
# -----------------------------------------------------------------------
# RELEASE ACTION (stops hold loop)
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ action_release != ''
and action_id == action_release
and options_reference_light is not none }}
sequence:
# Execute any_action_callback
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# -----------------------------------------------------------------------
# LIST ACTIONS (brightness, color temp, or hue list navigation)
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ action_id in [action_brightness_list_up, action_brightness_list_down, action_color_temp_list_up, action_color_temp_list_down, action_color_list_up, action_color_list_down]
and is_any_light_on
and options_reference_light is not none }}
sequence:
- variables:
list_control_loop: !input list_control_loop
brightness_list_entity: !input brightness_list_entity
color_temp_list_entity: !input color_temp_list_entity
color_list_entity: !input color_list_entity
is_brightness: "{{ action_id in [action_brightness_list_up, action_brightness_list_down] }}"
is_color_temp: "{{ action_id in [action_color_temp_list_up, action_color_temp_list_down] }}"
is_hue: "{{ action_id in [action_color_list_up, action_color_list_down] }}"
# Guards: ensure list entity is configured
- choose:
- conditions:
- condition: template
value_template: '{{ is_brightness and (brightness_list_entity is none) }}'
sequence:
- stop: "Action is trying to adjust brightness list up/down, but list itself is not set"
- conditions:
- condition: template
value_template: '{{ is_color_temp and (color_temp_list_entity is none) }}'
sequence:
- stop: "Action is trying to adjust color temperature list up/down, but list itself is not set"
- conditions:
- condition: template
value_template: '{{ is_hue and (color_list_entity is none) }}'
sequence:
- stop: "Action is trying to adjust hue list up/down, but list itself is not set"
- variables:
# Build operation state: [min, max, list_entity, attribute, is_up, is_same_mode, state_prefix]
operation_state: >
{% if is_brightness %}
{{ [
result_min_brightness,
result_max_brightness,
brightness_list_entity,
'brightness',
action_id == action_brightness_list_up,
true,
''] }}
{% elif is_color_temp %}
{{ [
result_min_color_temp,
result_max_color_temp,
color_temp_list_entity,
'color_temp_kelvin',
action_id == action_color_temp_list_up,
now_is_color_temp_mode,
state_key_color_temp_mode_prefix ] }}
{% elif is_hue %}
{{ [
result_min_hue,
result_max_hue,
color_list_entity,
'hs_color',
action_id == action_color_list_up,
now_is_rgb_mode,
state_key_hue_mode_prefix ]
}}
{% else %}
{{ [] }}
{% endif %}
min_value: "{{ operation_state[0] | int }}"
max_value: "{{ operation_state[1] | int }}"
value_sequence: "{{ state_attr(operation_state[2], 'options') | map('int') | list }}"
result_attribute: "{{ operation_state[3] | string }}"
is_step_increment: "{{ operation_state[4] | bool }}"
has_switched_mode: "{{ not (operation_state[5] | bool) }}"
state_key_prefix: "{{ operation_state[6] | string }}"
state_key_index: "{{ state_key_prefix + state_postfix_last_list_index }}"
state_key_brightness: "{{ state_key_prefix + state_postfix_brightness }}"
# Get current value from light
current_value: >
{% set v = state_attr(options_reference_light, result_attribute) %}
{% if v is none %}
{{ 0 }}
{% elif is_hue %}
{{ v[0] | int }}
{% else %}
{{ v | int }}
{% endif %}
# Calculate current index (from state if mode switched, otherwise find nearest)
current_index: >
{% if has_switched_mode %}
{{ automation_state.get(state_key_index, 0) | int }}
{% else %}
{% set ns = namespace(nearest_index=-1,nearest_diff=9999) %}
{% for i in range(value_sequence|length) %}
{% set diff = (value_sequence[i] - current_value) | abs %}
{% if diff <= ns.nearest_diff %}
{% set ns.nearest_diff = diff %}
{% set ns.nearest_index = i %}
{% endif %}
{% endfor %}
{{ ns.nearest_index }}
{% endif %}
# Calculate next index
index_step: >
{% if has_switched_mode %}
{{ 0 }}
{% else %}
{{ 1 if is_step_increment else -1 }}
{% endif %}
next_index_preview: "{{ current_index + index_step }}"
next_index_clamped: >
{% if list_control_loop %}
{% if index_step > 0 %}
{{ next_index_preview % (value_sequence | length) }}
{% else %}
{{ ((value_sequence | length) - 1) if next_index_preview < 0 else next_index_preview }}
{% endif %}
{% else %}
{{ [0, [(value_sequence | length - 1), next_index_preview]|min]|max }}
{% endif %}
next_value_preview: "{{ value_sequence[next_index_clamped] | round(0) }}"
current_value_clamped: "{{ [min_value, [max_value, current_value]|min]|max }}"
next_value_preview_clamped: "{{ [min_value, [max_value, next_value_preview]|min]|max }}"
# Handle edge case: if clamped value equals current, wrap around
next_index: >
{% if next_value_preview_clamped == current_value_clamped %}
{% set corrected_index = 0 if is_step_increment else value_sequence | length - 1 %}
{{ corrected_index }}
{% else %}
{{ next_index_clamped }}
{% endif %}
next_value: "{{ value_sequence[next_index] | int }}"
next_value_clamped: "{{ [min_value, [max_value, next_value]|min]|max }}"
value_to_set: >
{% if is_hue %}
{{ [next_value_clamped, default_saturation] }}
{% else %}
{{ next_value_clamped }}
{% endif %}
# Handle brightness when switching color modes
current_brightness: >
{% if state_key_prefix == '' %}
{{ next_value_clamped }}
{% else %}
{{ state_attr(options_reference_light, 'brightness') | int }}
{% endif %}
brightness_to_set: >
{% if has_switched_mode and state_key_prefix != '' %}
{{ automation_state.get(state_key_brightness, current_brightness) | int }}
{% else %}
{{ current_brightness }}
{% endif %}
light_data: >
{% set d = dict() %}
{% if transition_duration > 0 %}
{% set d = d | combine({ 'transition': transition_duration }) %}
{% endif %}
{% set d = d | combine({ result_attribute: value_to_set }) %}
{% if has_switched_mode and state_key_prefix != '' %}
{% set d = d | combine({ 'brightness': brightness_to_set }) %}
{% endif %}
{{ d }}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (List)"
message: >
operation_state = {{ operation_state }},
state_key_prefix = {{ state_key_prefix }},
has_switched_mode = {{ has_switched_mode }},
light_data = {{ light_data }}
# Save persistent state
- choose:
- conditions:
- condition: template
value_template: "{{ state_key_prefix != '' }}"
sequence:
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({ state_key_index: next_index, now_mode_state_prefix + state_postfix_brightness: current_brightness })) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# Apply value to lights
- service: light.turn_on
target:
entity_id: "{{ lights_to_control }}"
data: "{{ light_data }}"
# Execute callbacks
- choose:
- conditions:
- condition: template
value_template: "{{ is_brightness }}"
sequence: !input action_brightness_list_callback
- choose:
- conditions:
- condition: template
value_template: "{{ is_color_temp }}"
sequence: !input action_color_temp_list_callback
- choose:
- conditions:
- condition: template
value_template: "{{ is_hue }}"
sequence: !input action_color_list_callback
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# -----------------------------------------------------------------------
# INCREMENT ACTIONS (brightness, color temp, or hue increment/decrement)
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ (action_id in [action_brightness_increase, action_brightness_decrease, action_color_temp_increase, action_color_temp_decrease, action_hue_increase, action_hue_decrease])
and is_any_light_on
and options_reference_light != '' }}
sequence:
- variables:
is_brightness: "{{ action_id in [action_brightness_increase, action_brightness_decrease] }}"
brightness_step_override: !input brightness_step_override
action_brightness_increment_callback: !input action_brightness_increment_callback
mir_const: 1000000 # Mireds conversion constant
is_color_temp: "{{ action_id in [action_color_temp_increase, action_color_temp_decrease] }}"
color_temp_step_override: !input color_temp_step_override
action_color_temp_increment_callback: !input action_color_temp_increment_callback
is_hue: "{{ action_id in [action_hue_increase, action_hue_decrease] }}"
hue_step_override: !input hue_step_override
action_hue_increment_callback: !input action_hue_increment_callback
# Build operation state: [min, max, step_override, attribute, is_increment, should_act]
operation_state: >
{% set color_mode = state_attr(options_reference_light, 'color_mode') %}
{% if is_brightness %}
{{ [result_min_brightness, result_max_brightness, brightness_step_override | int, 'brightness', action_id == action_brightness_increase, true] }}
{% elif is_color_temp %}
{{ [result_min_color_temp, result_max_color_temp, color_temp_step_override | int, 'color_temp_kelvin', action_id == action_color_temp_increase, color_mode in ['color_temp']] }}
{% elif is_hue %}
{{ [result_min_hue, result_max_hue, hue_step_override | int, 'hs_color', action_id == action_hue_increase, color_mode in rgb_color_modes] }}
{% else %}
{{ [] }}
{% endif %}
min_value: "{{ operation_state[0] | int }}"
max_value: "{{ operation_state[1] | int }}"
step_override: "{{ operation_state[2] | int }}"
result_attribute: "{{ operation_state[3] | string }}"
is_increment: "{{ operation_state[4] | bool }}"
should_act: "{{ operation_state[5] | bool }}"
# Calculate step (convert mireds for color temp if needed)
step: >
{% if not should_act %}
{{ 0 }}
{% else %}
{% set fixed_action_step_size = ((mir_const / result_action_step_size) | int) if is_color_temp else result_action_step_size %}
{{ step_override if step_override != 0 else fixed_action_step_size }}
{% endif %}
real_step: "{{ step if is_increment else -step }}"
current_value: >
{% set v = state_attr(options_reference_light, result_attribute) %}
{% if v is none %}
{{ 0 }}
{% elif is_hue %}
{{ v[0] | int }}
{% else %}
{{ v | int }}
{% endif %}
next_value: "{{ (current_value + real_step) | int }}"
next_value_clamped: "{{ [min_value, [max_value, next_value]|min]|max }}"
value_to_set: >
{% if is_hue %}
{{ [next_value_clamped, default_saturation] }}
{% else %}
{{ next_value_clamped }}
{% endif %}
light_data: >
{% set d = dict() %}
{% if transition_duration > 0 %}
{% set d = d | combine({ 'transition': transition_duration }) %}
{% endif %}
{% set d = d | combine({ result_attribute: value_to_set }) %}
{{ d }}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Increment)"
message: >
result_action_step_size = {{ result_action_step_size }},
options_reference_light = {{ options_reference_light }},
operation_state = {{ operation_state }},
current_value = {{ current_value }},
real_step = {{ real_step }},
is_increment = {{ is_increment }},
next_value = {{ next_value }},
next_value_clamped = {{ next_value_clamped }},
light_data = {{ light_data }}
# Apply value to lights
- service: light.turn_on
target:
entity_id: "{{ lights_to_control }}"
data: "{{ light_data }}"
# Execute callbacks
- choose:
- conditions:
- condition: template
value_template: "{{ action_brightness_increment_callback != [] and is_brightness }}"
sequence: !input action_brightness_increment_callback
- choose:
- conditions:
- condition: template
value_template: "{{ action_color_temp_increment_callback != [] and is_color_temp }}"
sequence: !input action_color_temp_increment_callback
- choose:
- conditions:
- condition: template
value_template: "{{ action_hue_increment_callback != [] and is_hue }}"
sequence: !input action_hue_increment_callback
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# -----------------------------------------------------------------------
# PRESET LIST ACTIONS
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ action_id in [action_preset_list_up, action_preset_list_down]
and is_any_light_on
and options_reference_light is not none }}
sequence:
- variables:
list_control_loop: !input list_control_loop
preset_list_entity: !input preset_list_entity
action_preset_list_callback: !input action_preset_list_callback
# Guard: ensure preset list entity is configured
- choose:
- conditions:
- condition: template
value_template: '{{ preset_list_entity is none }}'
sequence:
- stop: "Action is trying to change preset list up/down, but list itself is not set"
- variables:
value_sequence: "{{ state_attr(preset_list_entity, 'options') | list }}"
is_increment: "{{ action_id == action_preset_list_up }}"
index_step: "{{ 1 if is_increment else -1 }}"
current_index: "{{ automation_state.get(state_key_preset_index, 0) | int }}"
next_index_preview: "{{ current_index + index_step }}"
next_index: >
{% if list_control_loop %}
{% if index_step > 0 %}
{{ next_index_preview % (value_sequence | length) }}
{% else %}
{{ ((value_sequence | length) - 1) if next_index_preview < 0 else next_index_preview }}
{% endif %}
{% else %}
{{ [0, [(value_sequence | length - 1), next_index_preview]|min]|max }}
{% endif %}
# Parse preset JSON format
preset_str: "{{ value_sequence[next_index] | trim }}"
light_data: >
{% set ns = namespace(res=dict(), brightness_found=False) %}
{% set parsed = preset_str | from_json %}
{% for key, value in parsed.items() %}
{% if key == 'brightness' %}
{% set ns.res = ns.res | combine({ 'brightness': value | int }) %}
{% set ns.brightness_found = True %}
{% elif key in ['color_temp_kelvin', 'effect', 'transition'] %}
{% set ns.res = ns.res | combine({ key: value }) %}
{% elif key in ['rgb_color', 'hs_color', 'xy_color'] %}
{% set ns.res = ns.res | combine({ key: value }) %}
{% endif %}
{% endfor %}
{# Preserve current brightness if not specified in preset #}
{% if not ns.brightness_found %}
{% set existing_brightness = state_attr(options_reference_light, 'brightness') | int %}
{% set ns.res = ns.res | combine({ 'brightness': existing_brightness }) %}
{% endif %}
{# Add transition if not specified in preset #}
{% if 'transition' not in ns.res and transition_duration > 0 %}
{% set ns.res = ns.res | combine({ 'transition': transition_duration }) %}
{% endif %}
{{ ns.res }}
# Apply preset to lights
- service: light.turn_on
target:
entity_id: "{{ lights_to_control }}"
data: "{{ light_data }}"
# Save preset index to persistent state
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{% set new_automation_state = (automation_state | combine({ state_key_preset_index: next_index })) %}
{{ automation_state_global | combine({ automation_state_key: new_automation_state }) | tojson }}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Preset Increment)"
message: >
current_index = {{ current_index }},
next_index = {{ next_index }},
light_data = {{ light_data }}
- choose:
- conditions:
- condition: template
value_template: "{{ action_preset_list_callback != [] }}"
sequence: !input action_preset_list_callback
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# -----------------------------------------------------------------------
# SCENE ACTIVATION ACTIONS
# -----------------------------------------------------------------------
- conditions:
- condition: template
value_template: >
{{ action_id != '' and action_id in [action_scene_1, action_scene_2, action_scene_3, action_scene_4] }}
sequence:
- variables:
action_scene_1_callback: !input action_scene_1_callback
action_scene_2_callback: !input action_scene_2_callback
action_scene_3_callback: !input action_scene_3_callback
action_scene_4_callback: !input action_scene_4_callback
scene_to_activate: >
{% if action_id == action_scene_1 %}
{{ scene_entity_1 }}
{% elif action_id == action_scene_2 %}
{{ scene_entity_2 }}
{% elif action_id == action_scene_3 %}
{{ scene_entity_3 }}
{% elif action_id == action_scene_4 %}
{{ scene_entity_4 }}
{% else %}
{{ none }}
{% endif %}
# Activate the scene
- choose:
- conditions:
- condition: template
value_template: "{{ scene_to_activate is not none }}"
sequence:
- service: scene.turn_on
target:
entity_id: "{{ scene_to_activate }}"
data:
transition: "{{ transition_duration }}"
# Execute scene callbacks
- choose:
- conditions:
- condition: template
value_template: "{{ action_id == action_scene_1 and action_scene_1_callback != [] }}"
sequence: !input action_scene_1_callback
- conditions:
- condition: template
value_template: "{{ action_id == action_scene_2 and action_scene_2_callback != [] }}"
sequence: !input action_scene_2_callback
- conditions:
- condition: template
value_template: "{{ action_id == action_scene_3 and action_scene_3_callback != [] }}"
sequence: !input action_scene_3_callback
- conditions:
- condition: template
value_template: "{{ action_id == action_scene_4 and action_scene_4_callback != [] }}"
sequence: !input action_scene_4_callback
- choose:
- conditions:
- condition: template
value_template: "{{ any_action_callback != [] }}"
sequence: !input any_action_callback
# ===========================================================================
# CUSTOM ACTIONS HANDLER
# ===========================================================================
- choose:
- conditions:
- condition: template
value_template: "{{ action_id in [action_custom_1, action_custom_2, action_custom_3, action_custom_4] }}"
sequence:
- variables:
action_custom_callback_1: !input action_custom_callback_1
action_custom_callback_2: !input action_custom_callback_2
action_custom_callback_3: !input action_custom_callback_3
action_custom_callback_4: !input action_custom_callback_4
- choose:
# Custom Action 1
- conditions:
- condition: template
value_template: "{{ action_id == action_custom_1 and action_custom_callback_1 != [] }}"
sequence: !input action_custom_callback_1
# Custom Action 2
- conditions:
- condition: template
value_template: "{{ action_id == action_custom_2 and action_custom_callback_2 != [] }}"
sequence: !input action_custom_callback_2
# Custom Action 3
- conditions:
- condition: template
value_template: "{{ action_id == action_custom_3 and action_custom_callback_3 != [] }}"
sequence: !input action_custom_callback_3
# Custom Action 4
- conditions:
- condition: template
value_template: "{{ action_id == action_custom_4 and action_custom_callback_4 != [] }}"
sequence: !input action_custom_callback_4
# ===========================================================================
# STORE LAST ACTION STEP SIZE
# ===========================================================================
# Persists the action step size for use in subsequent actions
# (useful when release action doesn't include step size)
- choose:
- conditions:
- condition: template
value_template: "{{ automation_state_entity != '' and action_step_size != 0 }}"
sequence:
- variables:
# Refresh global state (may have changed during action)
automation_state_global_updated: >
{% if automation_state_entity is not none %}
{% set text = states(automation_state_entity) | string %}
{% if text in ['unknown','unavailable','none',''] %}
{{ dict() }}
{% else %}
{{ text | from_json }}
{% endif %}
{% else %}
{{ dict() }}
{% endif %}
automation_state_updated: "{{ automation_state_global_updated.get(automation_state_key, dict()) }}"
new_automation_state: "{{ (automation_state_updated | combine({ state_key_last_action_step_size: action_step_size })) }}"
new_automation_state_global: "{{ automation_state_global_updated | combine({ automation_state_key: new_automation_state }) | tojson }}"
# Save updated state
- service: input_text.set_value
target:
entity_id: "{{ automation_state_entity }}"
data:
value: >
{{ new_automation_state_global }}
# Debug logging
- choose:
- conditions:
- condition: template
value_template: "{{ is_debug }}"
sequence:
- service: persistent_notification.create
data:
title: "Debug Info (Store last action size)"
message: >
new_automation_state_global = {{ new_automation_state_global }}