The scope of the package is to be able to control lights with both motion sensors and smart switches (optional: light threshold depending on sun component or sensor can be applied).
Also, this should help improving electricity waste as lights will turn off even if the person exiting the room forgot to turn them off; although there’s a trade off between convenience (having the lights if someone is in the room) and efficiency (turn lights off when not needed), adding a motion sensor can improve on both.
The full setup is made of a room_name package and a global_settings package. If not familiar with packages see https://www.home-assistant.io/docs/configuration/packages/
In order to set for multiple rooms duplicate the room_name_package for each room, rename the package and its variables to the relevant values (global_settings_package contains only variables applicable to the entire house thus it doesn’t need significant changes) or use part of the automations as considered suitable.
I tried to comment most of the components in order to explain their usage and, mostly, their purpose. Feel free to criticize if I haven’t done a good job on all of them (not a software developer by any means so it is kind of difficult to “feel” the ideal amount of commenting).
Main functionalities:
- control lights from both motion sensors (PIR or microwave) and smart switches without flickering when the active controlling unit changes;
- have multiple motion sensors controlling the lights (case of non-standard form rooms such as L or T-shape where a single motion sensor is not sufficient to cover the entire space) with a resetable script and/or turn the lights on if insufficient light (in combination with a light sensor and/or sun component); also, set multiple way (2 way/3 way/n way) switches - “hotel like switching”;
- modular setup, if no PIR available (motion sensor specific areas are indicated);
- can use threshold for light (either sun dependent or measured with light sensor; I use a LDR attached to NodeMCU running OpenMQTTGateway https://github.com/1technophile/OpenMQTTGateway);
- override switch for blocking motion sensor (and use only smart switches for controlling lights); this also can allow using motion sensors for alarm triggering only;
- light platform independent (there are some MiLight specific components but they’re commented) and switch/motion sensor agnostic;
- separate settings for RGB vs. CCT lights;
- there are different timers based on the method of turning the lights on (lower timer value if lights are triggered by motion sensor vs. a higher timer value if triggered by switch);
- case timer set for switch is near end and the motion sensor is triggered (or the timer for the switch is lower than the timer for motion sensor), then the control for the lights is transferred to motion sensor (via resetable script so that each time motion sensor is triggered the light will be set to the value of the timer);
- all lights are set to brightness 1 before turning off (most smart lights turn on with the previous settings used before off in order to be able to use with “dumb” switches; Philips has sometimes recently added a feature to turn lights on with defined settings but most of the other type of smart lights use the last known settings); having brightness at 1 before sending other commands to lights ensures both smoother transitions and keeps inconveniences associated with power failures at minimum (otherwise lights could turn on at previous known settings in the middle of the night);
- each individual value for R, G and B is based on input_number (there are several Python solutions that can be used for storing variables but input_number is still easy to use); the sliders for R, G and B can be controlled separately from other automations and can have multiple uses:
- controlling all lights in the house in one move (in my case with MiLight groups) by forwarding color changes to the Group ID that is paired to all the lights in the house and changing individual R, G, B components that are used when the lights are turned on rather than turning the lights on (and annoying anyone who’s sleeping). If wanting to change multiple (turned on) lights it can be achieved either by changing them individually or changing the group defined in HA; the problem is that for more than 3-4 lights, on relatively under powered hosts such as Rpis, it would take near 10 seconds to change the values for all the entities in the group. In my case I used native groups of lights (outside HA) which would turn the lights on/off or change the colors in a single step;
- set different lights color based on time of day (morning/day/sunset/night - these settings are not included in the package but I can provide them if interested) or through Circadian Lighting: https://community.home-assistant.io/t/circadian-lighting-custom-component/61246;
- set different accents for Halloween, Christmas or warmer/colder lights during winter/summer (separate or in combination with the above);
Although it would be equally easy to use an input_boolean instead of a MQTT, the MQTT topics can be easily accessed from another system (either Home Assistant or OH, Domotic(x)).
While the switch will toggle the lights, the functionality of the motion sensor is summarized below (probably it would have been preferable a NodeRed flow; however, I started using Home Assistant prior to its introduction and I feel comfortable writing automations).
room_name_package
homeassistant:
sensor:
#ldr# - platform: mqtt
#ldr# state_topic: 'home/room_name/adctomqtt'
#ldr# name: 'sensor_ldr_room_name'
#ldr# unit_of_measurement: 'lux'
#ldr# value_template: '{{ 1024 - value | float }}'
# Uncomment '#ldr#' to use the light sensor in the package
input_number:
room_name_light_sensitivity:
name: 'room_name_light_sensitivity'
min: 100
max: 1000
step: 50
initial: 200
mode: slider
#binary_sensor:
#Define binary_sensor here based on the specific configuration.
switch:
#PIR override switch;
- platform: mqtt
name: "switch_pir_override_room_name"
state_topic: "home/room_name/pir_override/state"
command_topic: "home/room_name/pir_override"
payload_on: "on"
payload_off: "off"
optimistic: true
qos: 0
retain: true
#Switch lights; although use of a virtual switch to keep track of the physical devices is not mandatory, it simplifies control (as it centralizes all physical switches and can be used in the frontend to simultaneously turn the light on and set the timer).
- platform: mqtt
name: "switch_lights_room_name"
state_topic: "home/room_name/lights_control/state"
command_topic: "home/room_name/lights_control"
payload_on: "on"
payload_off: "off"
optimistic: true
qos: 0
retain: true
#Switch color_temp in order to use color temperature mode for bulbs (if supported)
- platform: mqtt
name: "color_temp_room_name"
state_topic: "home/room_name/color_temp/state"
command_topic: "home/room_name/color_temp"
payload_on: "on"
payload_off: "off"
optimistic: true
qos: 0
retain: true
script:
# General script
## Turns off all running scripts; sets brightness to 1 then turns the light off
lights_off_room_name:
sequence:
- service: script.turn_off
entity_id: script.timer_off_sw_room_name
- service: script.turn_off
entity_id: script.timer_ct_switch_room_name
- service: script.turn_off
entity_id: script.timer_rgb_switch_room_name
#start of PIR section; comment following if not using PIR
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: script.turn_off
entity_id: script.timer_ct_pir_room_name
- service: script.turn_off
entity_id: script.timer_rgb_pir_room_name
#end of PIR section
- service: light.turn_on
data:
entity_id: light.light_room_name
brightness: 1
- delay:
milliseconds: 100
- service: light.turn_off
entity_id: light.light_room_name
# Timers scripts (use the value of the input number to schedule lights off)
##1. Timer to turn lights off when switch was used (use the value of the input_number to schedule lights off)
timer_off_sw_room_name:
sequence:
- service: script.turn_off
entity_id: script.lights_off_room_name
- service: script.turn_off
entity_id: script.timer_ct_switch_room_name
- service: script.turn_off
entity_id: script.timer_rgb_switch_room_name
#start of PIR section; comment following if not using PIR
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: script.turn_off
entity_id: script.timer_ct_pir_room_name
- service: script.turn_off
entity_id: script.timer_rgb_pir_room_name
#end of PIR section
# switch will turn off the lights regardless if PIR or switch turned them on initially
- delay: '{{(states.input_number.switch_timer.state|int/60)|int}}:{{(states.input_number.switch_timer.state | int)%60 }}:00'
- service: switch.turn_off
entity_id: switch.switch_lights_room_name
##2. Timer to turn lights off when PIR was used (use the value of the input number to schedule lights off)
#Comment if not using PIR
timer_off_pir_room_name:
sequence:
- delay: '{{(states.input_number.pir_timer.state|int/60)|int}}:{{(states.input_number.pir_timer.state | int)%60 }}:00'
- service: script.turn_on
entity_id: script.lights_off_room_name
##3.a. Timer when switch was used in CT mode
timer_ct_switch_room_name:
sequence:
- service: light.turn_on
data_template:
effect: "white_mode"
entity_id: light.light_room_name
brightness: '{{ states.input_number.dimmer.state | int }}'
color_temp: '{{ states.input_number.color.state | int }}'
- service: script.turn_on
entity_id: script.timer_off_sw_room_name
##3.b. Timer when switch was used in RGB mode
timer_rgb_switch_room_name:
sequence:
- service: light.turn_on
data_template:
entity_id: light.light_room_name
brightness: '{{ states.input_number.dimmer.state | int }}'
rgb_color: ['{{states.input_number.r.state | int}}','{{states.input_number.g.state | int}}','{{states.input_number.b.state | int}}']
- service: script.turn_on
entity_id: script.timer_off_sw_room_name
##4.a. Timer for PIR in CT mode
#Comment if not using PIR
timer_ct_pir_room_name:
sequence:
# Cancel old timer
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: light.turn_on
data_template:
effect: "white_mode"
entity_id: light.light_room_name
brightness: '{{ states.input_number.dimmer.state | int }}'
color_temp: '{{ states.input_number.color.state | int }}'
# Set new timer
- service: script.turn_on
entity_id: script.timer_off_pir_room_name
##4.b. Timer for PIR in RGB mode
#Comment if not using PIR
timer_rgb_pir_room_name:
sequence:
# Cancel old timer
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: light.turn_on
data_template:
entity_id: light.light_room_name
brightness: '{{ states.input_number.dimmer.state | int }}'
rgb_color: ['{{states.input_number.r.state | int}}','{{states.input_number.g.state | int}}','{{states.input_number.b.state | int}}']
# Set new timer
- service: script.turn_on
entity_id: script.timer_off_pir_room_name
automation:
# MiLight specific: Send turn off to lights that are already off (case previous off command didn't reached the bulb)
#milight_specific# - alias: room_name lights set to off
#milight_specific# initial_state: 'on'
#milight_specific# trigger:
#milight_specific# - platform: time_pattern
#milight_specific# minutes: '/5'
#milight_specific# seconds: 17
#milight_specific# condition:
#milight_specific# condition: and
#milight_specific# conditions:
#milight_specific# - condition: state
#milight_specific# entity_id: light.light_room_name
#milight_specific# state: 'off'
#milight_specific# action:
#milight_specific# - service: mqtt.publish
#milight_specific# data:
#milight_specific# topic: "milight_gw/0x2/rgb_cct/4"
#milight_specific# payload: '{"state":"off"}'
# Turn off lights if both lights and switch are on
- alias: room_name turn off lights
initial_state: 'on'
trigger:
#Replace with own defined binary_sensor/sensor/switch
- platform: state
entity_id: binary_sensor.binary_sensor_switch_1_room_name, binary_sensor.binary_sensor_switch_2_room_name, binary_sensor.binary_sensor_switch_3_room_name
to: 'on'
- platform: state
entity_id: sensor.sensor_switch_1_room_name, sensor.sensor_switch_2_room_name, sensor.sensor_switch_3_room_name
to: 'single'
- platform: state
entity_id: switch.switch_1_room_name, switch.switch_2_room_name, switch.switch_3_room_name
#The multiple entities used for trigger achieve the 3way switching; the number of physical switches that can be used here is not limited. Pressing any of the switches will toggle the light regardless of the switch that was used before.
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
#The switch used here is virtual, not tied to any hardware switch;
- condition: state
entity_id: switch.switch_lights_room_name
state: 'on'
action:
- service: switch.turn_off
entity_id: switch.switch_lights_room_name
- alias: room_name turn off lights if lights are on and switch is off
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_switch_1_room_name, binary_sensor.binary_sensor_switch_2_room_name, binary_sensor.binary_sensor_switch_3_room_name
to: 'on'
- platform: state
entity_id: sensor.sensor_switch_1_room_name, sensor.sensor_switch_2_room_name, sensor.sensor_switch_3_room_name
to: 'single'
- platform: state
entity_id: switch.switch_1_room_name, switch.switch_2_room_name, switch.switch_3_room_name
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'off'
action:
- service: script.turn_on
entity_id: script.lights_off_room_name
- alias: room_name turn on lights if both lights and switch are off
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_switch_1_room_name, binary_sensor.binary_sensor_switch_2_room_name, binary_sensor.binary_sensor_switch_3_room_name
to: 'on'
- platform: state
entity_id: sensor.sensor_switch_1_room_name, sensor.sensor_switch_2_room_name, sensor.sensor_switch_3_room_name
to: 'single'
- platform: state
entity_id: switch.switch_1_room_name, switch.switch_2_room_name, switch.switch_3_room_name
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'off'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'off'
action:
- service: switch.turn_on
entity_id: switch.switch_lights_room_name
- alias: room_name turn on lights if switch is already on in CT mode
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_switch_1_room_name, binary_sensor.binary_sensor_switch_2_room_name, binary_sensor.binary_sensor_switch_3_room_name
to: 'on'
- platform: state
entity_id: sensor.sensor_switch_1_room_name, sensor.sensor_switch_2_room_name, sensor.sensor_switch_3_room_name
to: 'single'
- platform: state
entity_id: switch.switch_1_room_name, switch.switch_2_room_name, switch.switch_3_room_name
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'off'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'on'
- condition: state
entity_id: switch.color_temp_room_name
state: 'on'
action:
- service: script.turn_on
entity_id: script.timer_ct_switch_room_name
- alias: room_name turn on lights if switch is already on in RGB mode
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_switch_1_room_name, binary_sensor.binary_sensor_switch_2_room_name, binary_sensor.binary_sensor_switch_3_room_name
to: 'on'
- platform: state
entity_id: sensor.sensor_switch_1_room_name, sensor.sensor_switch_2_room_name, sensor.sensor_switch_3_room_name
to: 'single'
- platform: state
entity_id: switch.switch_1_room_name, switch.switch_2_room_name, switch.switch_3_room_name
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'off'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'on'
- condition: state
entity_id: switch.color_temp_room_name
state: 'off'
action:
- service: script.turn_on
entity_id: script.timer_rgb_switch_room_name
##Switch turns on lights and sets delay to PIR timer if switch timer is lower than PIR timer
- alias: room_name turn switch to PIR timer in CT mode
initial_state: 'on'
trigger:
- platform: state
entity_id: switch.switch_lights_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: switch.color_temp_room_name
state: 'on'
- condition: template
value_template: '{{(states.input_number.switch_timer.state) | int < (states.input_number.pir_timer.state| int)}}'
action:
- service: script.turn_on
entity_id: script.timer_ct_pir_room_name
- alias: room_name turn switch to PIR timer in RGB mode
initial_state: 'on'
trigger:
- platform: state
entity_id: switch.switch_lights_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: switch.color_temp_room_name
state: 'off'
- condition: template
value_template: '{{(states.input_number.switch_timer.state) | int < (states.input_number.pir_timer.state| int)}}'
action:
- service: script.turn_on
entity_id: script.timer_rgb_pir_room_name
#Switch timer set
- alias: room_name switch timer in CT mode
initial_state: 'on'
trigger:
- platform: state
entity_id: switch.switch_lights_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: switch.color_temp_room_name
state: 'on'
action:
- service: script.turn_on
entity_id: script.timer_ct_switch_room_name
- alias: room_name switch timer in CT mode
initial_state: 'on'
trigger:
- platform: state
entity_id: switch.switch_lights_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: switch.color_temp_room_name
state: 'off'
action:
- service: script.turn_on
entity_id: script.timer_rgb_switch_room_name
#Turn on lights if PIR sensor is triggered and lights are off
#Comment if not using PIR
- alias: room_name PIR trigger in CT mode
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_pir_1_room_name, binary_sensor.binary_sensor_pir_2_room_name, binary_sensor.binary_sensor_pir_3_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'off'
- condition: or
conditions:
- condition: state
entity_id: sun.sun
state: 'below_horizon'
#ldr# - condition: template
#ldr# value_template: '{{(states.sensor.sensor_ldr_room_name.state | int) < (states.input_number.room_name_light_sensitivity.state | int)}}'
- condition: state
entity_id: switch.switch_pir_override_room_name
state: 'off'
- condition: state
entity_id: switch.color_temp_room_name
state: 'on'
action:
- service: script.turn_on
entity_id: script.timer_ct_pir_room_name
- alias: room_name PIR trigger in RGB mode
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_pir_1_room_name, binary_sensor.binary_sensor_pir_2_room_name, binary_sensor.binary_sensor_pir_3_room_name
to: 'on'
#These binary sensors correspond to Kerui P819 physical PIR sensors. Any number of PIR sensors can be used as trigger (suitable for large rooms or non-standard form rooms where a single PIR cannot cover the entire surface or turning the lights only in a designated area - ie. by waiving hand below/above the bed).
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'off'
- condition: or
conditions:
- condition: state
entity_id: sun.sun
state: 'below_horizon'
#ldr# - condition: template
#ldr# value_template: '{{(states.sensor.sensor_ldr_room_name.state | int) < (states.input_number.room_name_light_sensitivity.state | int)}}'
- condition: state
entity_id: switch.switch_pir_override_room_name
state: 'off'
#By overriding the PIR function only switches (or HA) can turn the lights on.
- condition: state
entity_id: switch.color_temp_room_name
state: 'off'
action:
- service: script.turn_on
entity_id: script.timer_rgb_pir_room_name
#Send turn on again (case one of the bulbs didn't turn on properly)
#Comment if not using PIR
- alias: room_name pir trigger if insufficient brightness in CT mode
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_pir_1_room_name, binary_sensor.binary_sensor_pir_2_room_name, binary_sensor.binary_sensor_pir_3_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'off'
- condition: state
entity_id: switch.switch_pir_override_room_name
state: 'off'
- condition: state
entity_id: switch.color_temp_room_name
state: 'on'
#ldr# - condition: template
#ldr# value_template: '{{(states.sensor.sensor_ldr_room_name.state | int) < (states.input_number.room_name_light_sensitivity.state | int)}}'
action:
- service: script.turn_on
entity_id: script.timer_ct_pir_room_name
#Comment if not using PIR
- alias: room_name pir trigger if insufficient brightness in RGB mode
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_pir_1_room_name, binary_sensor.binary_sensor_pir_2_room_name, binary_sensor.binary_sensor_pir_3_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'off'
- condition: state
entity_id: switch.switch_pir_override_room_name
state: 'off'
- condition: state
entity_id: switch.color_temp_room_name
state: 'off'
#ldr# - condition: template
#ldr# value_template: '{{(states.sensor.sensor_ldr_room_name.state | int) < (states.input_number.room_name_light_sensitivity.state | int)}}'
action:
- service: script.turn_on
entity_id: script.timer_rgb_pir_room_name
#E.2 Keep lights on if PIR sensor is triggered and lights are on (triggered by SWITCH - then PIR takes over)
#Comment if not using PIR
- alias: room_name switch triggers pir takes over
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_pir_1_room_name, binary_sensor.binary_sensor_pir_2_room_name, binary_sensor.binary_sensor_pir_3_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'on'
- condition: state
entity_id: switch.switch_pir_override_room_name
state: 'off'
- condition: template
value_template: '{{(states.input_number.switch_timer.state | int - (as_timestamp(now())-as_timestamp(states.light.light_room_name.last_changed))/60) < (states.input_number.pir_timer.state| int)}}'
action:
- service: script.turn_off
entity_id: script.timer_off_sw_room_name
- service: script.turn_off
entity_id: script.timer_ct_switch_room_name
- service: script.turn_off
entity_id: script.timer_rgb_switch_room_name
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: script.turn_on
entity_id: script.timer_off_pir_room_name
#F.1 Keep lights on if PIR sensor is triggered and lights are on (previously triggered by PIR)
#Comment if not using PIR
- alias: room_name pir sensor trigger again
initial_state: 'on'
trigger:
- platform: state
entity_id: binary_sensor.binary_sensor_pir_1_room_name, binary_sensor.binary_sensor_pir_2_room_name, binary_sensor.binary_sensor_pir_3_room_name
to: 'on'
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
- condition: state
entity_id: switch.switch_lights_room_name
state: 'off'
- condition: state
entity_id: switch.switch_pir_override_room_name
state: 'off'
action:
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: script.turn_on
entity_id: script.timer_off_pir_room_name
#Turn off lights
- alias: room_name turn off lights
initial_state: 'on'
trigger:
- platform: state
entity_id: switch.switch_lights_room_name
to: 'off'
condition:
condition: and
conditions:
- condition: state
entity_id: light.light_room_name
state: 'on'
action:
- service: script.turn_on
entity_id: script.lights_off_room_name
# Terminate all scripts if light is turned off
- alias: room_name turn off scripts if lights are off
initial_state: 'on'
trigger:
- platform: state
entity_id: light.light_room_name
to: 'off'
action:
- service: script.turn_off
entity_id: script.lights_off_room_name
- service: script.turn_off
entity_id: script.timer_off_sw_room_name
- service: script.turn_off
entity_id: script.timer_ct_switch_room_name
- service: script.turn_off
entity_id: script.timer_rgb_switch_room_name
#start of PIR section; comment following if not using PIR
- service: script.turn_off
entity_id: script.timer_off_pir_room_name
- service: script.turn_off
entity_id: script.timer_ct_pir_room_name
- service: script.turn_off
entity_id: script.timer_rgb_pir_room_name
#end of PIR section
general_settings_package
homeassistant:
input_number:
dimmer:
name: 'Dimmer'
min: 0
max: 255
step: 15
mode: slider
color:
name: 'Color Temp'
min: 0
max: 370
step: 10
mode: slider
r:
name: 'Red'
min: 0
max: 255
step: 15
mode: slider
g:
name: 'Green'
min: 0
max: 255
step: 15
mode: slider
b:
name: 'Blue'
min: 0
max: 255
step: 15
mode: slider
pir_timer:
name: 'PIR Timer'
min: 1
#defines timer for lights if triggerred by PIR sensor; change max and initial as suitable
max: 61
step: 5
initial: 11
mode: slider
switch_timer:
name: 'Switch Timer'
min: 1
#defines timer for lights if triggerred by switch sensor; change max and initial as suitable
max: 121
step: 12
initial: 37
mode: slider