Last active
October 28, 2025 14:24
-
-
Save Belphemur/7b2db329f986214743f776507cc801fa to your computer and use it in GitHub Desktop.
Circadian Light Home Assistant
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| blueprint: | |
| name: "Circadian Motion Lighting" | |
| description: > | |
| **Version: 1.1** | |
| Motion/Occupancy/Presence-activated lighting with minute-by-minute circadian adjustments. | |
| Uses imperceptible gradual changes based on sun elevation for smooth transitions. | |
| **Features:** | |
| - Runs every minute for smooth, barely perceptible transitions | |
| - Color temperature transitions from cool (morning) to warm (evening) | |
| - Brightness dimming throughout the day | |
| - Lux sensor condition with hysteresis to prevent feedback loops | |
| - Motion/occupancy sensor integration with 'for' duration (no delays) | |
| - Debug mode with persistent notifications showing all variables | |
| Required = * | |
| domain: automation | |
| author: "Home Assistant User" | |
| homeassistant: | |
| min_version: "2025.10.0" | |
| input: | |
| # Motion Sensor Settings | |
| motion_settings: | |
| name: "Motion Sensor Settings *" | |
| icon: mdi:motion-sensor | |
| collapsed: true | |
| input: | |
| motion_sensor: | |
| name: "Motion/Occupancy/Presence Sensor *" | |
| description: The sensor that triggers the lights (motion, occupancy, or presence detection) | |
| selector: | |
| entity: | |
| filter: | |
| domain: binary_sensor | |
| device_class: | |
| - motion | |
| - occupancy | |
| - presence | |
| no_motion_delay: | |
| name: "No Motion Duration" | |
| description: How long motion must be off before turning off lights | |
| default: "00:05:00" | |
| selector: | |
| duration: | |
| # Light Settings | |
| light_settings: | |
| name: "Light Settings *" | |
| icon: mdi:lightbulb-outline | |
| collapsed: true | |
| input: | |
| light_entities: | |
| name: "Light Entities *" | |
| description: The lights to control | |
| selector: | |
| target: | |
| entity: | |
| domain: light | |
| # Lux Sensor Settings | |
| lux_settings: | |
| name: "Lux Sensor Settings *" | |
| icon: mdi:brightness-6 | |
| collapsed: true | |
| input: | |
| lux_sensor: | |
| name: "Illuminance Sensor *" | |
| description: Lux sensor to determine if lights are needed (same room as lights) | |
| selector: | |
| entity: | |
| filter: | |
| domain: sensor | |
| device_class: illuminance | |
| lux_threshold: | |
| name: "Lux Threshold" | |
| description: Maximum lux level to turn on lights | |
| default: 50 | |
| selector: | |
| number: | |
| min: 1 | |
| max: 500 | |
| unit_of_measurement: lux | |
| lux_hysteresis: | |
| name: "Lux Hysteresis" | |
| description: Lux buffer to prevent rapid on/off cycling (added to threshold for turn-off) | |
| default: 20 | |
| selector: | |
| number: | |
| min: 5 | |
| max: 100 | |
| unit_of_measurement: lux | |
| # Circadian Settings | |
| circadian_settings: | |
| name: "Circadian Settings" | |
| icon: mdi:weather-sunny | |
| collapsed: true | |
| input: | |
| start_brightness: | |
| name: "Starting Brightness" | |
| description: Maximum brightness percentage (morning/midday) | |
| default: 100 | |
| selector: | |
| number: | |
| min: 1 | |
| max: 100 | |
| unit_of_measurement: "%" | |
| end_brightness: | |
| name: "Ending Brightness" | |
| description: Minimum brightness percentage (evening/night) | |
| default: 30 | |
| selector: | |
| number: | |
| min: 1 | |
| max: 100 | |
| unit_of_measurement: "%" | |
| start_color_temp: | |
| name: "Starting Color Temperature" | |
| description: Cool color temperature for morning (in Kelvin) | |
| default: 5000 | |
| selector: | |
| number: | |
| min: 2000 | |
| max: 6500 | |
| unit_of_measurement: K | |
| end_color_temp: | |
| name: "Ending Color Temperature" | |
| description: Warm color temperature for evening (in Kelvin) | |
| default: 2700 | |
| selector: | |
| number: | |
| min: 2000 | |
| max: 6500 | |
| unit_of_measurement: K | |
| # Debug Settings | |
| debug_settings: | |
| name: "Debug Settings" | |
| icon: mdi:bug-outline | |
| collapsed: true | |
| input: | |
| debug_mode: | |
| name: "Debug Mode" | |
| description: Enable debug notifications showing all calculated variables | |
| default: false | |
| selector: | |
| boolean: | |
| # Automation Configuration | |
| mode: restart | |
| max_exceeded: silent | |
| variables: | |
| # Assign all !input values to variables first | |
| motion_sensor: !input motion_sensor | |
| light_entities: !input light_entities | |
| lux_sensor: !input lux_sensor | |
| lux_threshold: !input lux_threshold | |
| lux_hysteresis: !input lux_hysteresis | |
| no_motion_delay: !input no_motion_delay | |
| start_brightness: !input start_brightness | |
| end_brightness: !input end_brightness | |
| start_color_temp: !input start_color_temp | |
| end_color_temp: !input end_color_temp | |
| debug_mode: !input debug_mode | |
| # Calculate sun elevation-based values | |
| sun_elevation: "{{ state_attr('sun.sun', 'elevation') | float(0) }}" | |
| elevation_normalized: "{{ ((sun_elevation + 10) / 100) | float(0) }}" | |
| elevation_clamped: "{{ [0, [elevation_normalized, 1] | min] | max }}" | |
| # Calculate dynamic values using assigned variables | |
| color_temp_range: "{{ (start_color_temp | int) - (end_color_temp | int) }}" | |
| current_color_temp: "{{ (end_color_temp | int) + (color_temp_range * elevation_clamped) | int }}" | |
| brightness_range: "{{ (start_brightness | int) - (end_brightness | int) }}" | |
| current_brightness: "{{ (end_brightness | int) + (brightness_range * elevation_clamped) | int }}" | |
| # Lux thresholds | |
| lux_on_threshold: "{{ lux_threshold | int }}" | |
| lux_off_threshold: "{{ (lux_threshold | int) + (lux_hysteresis | int) }}" | |
| current_lux: "{{ states(lux_sensor) | float(0) }}" | |
| # Motion sensor state | |
| motion_state: "{{ states(motion_sensor) }}" | |
| # Check if any lights are currently on | |
| lights_on: "{{ expand(light_entities.entity_id) | selectattr('state', 'eq', 'on') | list | count > 0 }}" | |
| lights_on_count: "{{ expand(light_entities.entity_id) | selectattr('state', 'eq', 'on') | list | count }}" | |
| # Current time for debugging | |
| current_time: "{{ now().strftime('%H:%M:%S') }}" | |
| current_date: "{{ now().strftime('%Y-%m-%d') }}" | |
| trigger: | |
| # Motion/Occupancy/Presence detected | |
| - platform: state | |
| entity_id: !input motion_sensor | |
| from: "off" | |
| to: "on" | |
| id: "motion_on" | |
| # Motion/Occupancy/Presence stopped - FIXED: Using 'for' instead of delay | |
| - platform: state | |
| entity_id: !input motion_sensor | |
| from: "on" | |
| to: "off" | |
| for: !input no_motion_delay | |
| id: "motion_off" | |
| # Time-based trigger - runs every minute for gradual adjustments | |
| - platform: time_pattern | |
| minutes: "/1" | |
| id: "minute_update" | |
| condition: | |
| - condition: template | |
| value_template: "{{ true }}" | |
| action: | |
| # Debug notification - show all variables if debug mode is enabled | |
| - if: | |
| - condition: template | |
| value_template: "{{ debug_mode }}" | |
| then: | |
| - service: persistent_notification.create | |
| data: | |
| title: "π§ Circadian Lighting Debug" | |
| message: | | |
| **Trigger:** {{ trigger.id }} | |
| **Time:** {{ current_date }} {{ current_time }} | |
| **βοΈ Sun & Elevation:** | |
| β’ Sun Elevation: {{ sun_elevation }}Β° | |
| β’ Elevation Normalized: {{ elevation_normalized | round(3) }} | |
| β’ Elevation Clamped: {{ elevation_clamped | round(3) }} | |
| **π‘ Light Calculations:** | |
| β’ Current Color Temp: {{ current_color_temp }}K | |
| β’ Current Brightness: {{ current_brightness }}% | |
| β’ Color Temp Range: {{ color_temp_range }}K | |
| β’ Brightness Range: {{ brightness_range }}% | |
| **π Lux & Sensors:** | |
| β’ Current Lux: {{ current_lux }} lux | |
| β’ Lux ON Threshold: {{ lux_on_threshold }} lux | |
| β’ Lux OFF Threshold: {{ lux_off_threshold }} lux | |
| β’ Motion Sensor: {{ motion_state }} | |
| **π Light Status:** | |
| β’ Lights On: {{ lights_on }} | |
| β’ Lights On Count: {{ lights_on_count }} | |
| **βοΈ Configuration:** | |
| β’ Start Color: {{ start_color_temp }}K β End Color: {{ end_color_temp }}K | |
| β’ Start Brightness: {{ start_brightness }}% β End Brightness: {{ end_brightness }}% | |
| β’ Lux Threshold: {{ lux_threshold }} lux (Β±{{ lux_hysteresis }} hysteresis) | |
| β’ No Motion Duration: {{ no_motion_delay }} | |
| notification_id: "circadian_debug" | |
| - choose: | |
| # Motion sensor activated - turn on lights if dark enough | |
| - conditions: | |
| - condition: trigger | |
| id: "motion_on" | |
| - condition: template | |
| value_template: "{{ states(lux_sensor) | float(0) < lux_on_threshold }}" | |
| sequence: | |
| - service: light.turn_on | |
| target: !input light_entities | |
| data: | |
| brightness_pct: "{{ current_brightness }}" | |
| color_temp_kelvin: "{{ current_color_temp }}" | |
| transition: 2 | |
| # Motion sensor deactivated - FIXED: No delay needed, trigger handles timing | |
| - conditions: | |
| - condition: trigger | |
| id: "motion_off" | |
| - condition: template | |
| value_template: "{{ states(lux_sensor) | float(0) > lux_off_threshold }}" | |
| sequence: | |
| - service: light.turn_off | |
| target: !input light_entities | |
| data: | |
| transition: 5 | |
| # Minute-by-minute gradual adjustment - only if lights are on | |
| - conditions: | |
| - condition: trigger | |
| id: "minute_update" | |
| - condition: template | |
| value_template: "{{ lights_on }}" | |
| sequence: | |
| - service: light.turn_on | |
| target: !input light_entities | |
| data: | |
| brightness_pct: "{{ current_brightness }}" | |
| color_temp_kelvin: "{{ current_color_temp }}" | |
| transition: 60 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment