Hibernate your home with an advanced Home Center 2 scene to automatically turn off all lights (and save state)
Summary
After I build reliable presence detection I wanted to automatically turn off the lights when nobody is at home. I though it would be nice if the controller remembers the state of the house and returns to that state when the first person arrives back at home. When there is a lot of time passed a standard arrival routine will run, because the previous light state is not important anymore right then.
The scene checking for lights needs to be universal, so I can use it for a go to bed scene also. For this purpose the scene must handle exclusions. (you don’t want to turn of the light that you just turned on for your bedtime scene).
Goals
- Check all z-wave lights that are on.
- Check all Philips Hue lights that are on.
- Turn these lights off when everybody is away from home.
- Save the lights that where turned off to turn them back on when someone arrives at home.
- Exclude lights to turn off when using a go to bed scene trigger (make scene for global use).
TL;DR
- Create an universal scene for checking the lights.
- Create a scene for leaving home.
- Create a scene for arriving at home.
- Optionally create a go to bed scene._
- Save the light state to a global variable
How I implemented it
In words
When Node-RED detects that somebody left the house it sets a personal presence global variable to No
in Home Center 2. In my case, if both JoepPresent
and MoniquePresent
are set to No
the leave home scene is triggered. This scene sets another global variable HomeState
to away
. This is done to tackle the issue when you arrive at home and on the driveway your phone get’s connected to your home Wi-Fi the system responds immediately. You don’t want that to happen. You want to trigger the arrive home scene with a door- or motion sensor. Then it’s triggered as you physically step into your house.
The leaving home scene triggers the turn off all lights scene to save the light state. If the first person arrive’s back at home the arriving home scene is triggered by the earlier mentioned door- or motion sensor.
Devices used
I can enter my house from three doors. At the front door I use a Fibaro Door/Window Sensor and the other rooms are equipped with a Fibaro Motion Sensor. You have to change the sensors in my LUA scenes to your own use case.
Global variables
These are the global variables you need for the routine described on this page only. Presence detection variables are not described as you can have another presence detection system than what I use.
Global Variable | Type | Value |
---|---|---|
HomeState | Predefined | athome, away |
TurnOffExclusions | Normal | 0 |
SleepingLights | Normal | 0 |
Scene 1: check and turn off all lights explained
You can download the full LUA scenes at the bottom of this page. I only describe snippets of my code to make you understand what it does and show the challenges I ran into.
Function to check if value exists in array
I want to keep a table with all rooms that have lights on. This information is for future use, when I want to send a push message with rooms with lights still on message for example. To only add new rooms to the table I wrote the following function:
local function has_value (tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
Get exclusions from the global variable
Before starting this scene from within the leave home scene I set a global variable with the id’s of the lights that not have to turn off automatically (see later on this page), I read this global variable with the following code:
local exclusionsVar = fibaro:getGlobalValue("TurnOffExclusions")
local exclusions = {}
for match in (exclusionsVar..'|'):gmatch("(.-)"..'|') do
table.insert(exclusions, match);
end
Check Z-Wave lights
When the exclusions are read I can loop over all devices and read the isLight
value to determine if the device is a light:
if fibaro:getValue(i, "isLight") == "1" then
...
With this check I don’t have to keep a table with all light source id’s. When you add a light to your house it’s automatically incorporated in the scenes. Then I check if the device is on:
if (fibaro:getValue(i, "value") >= "1")
...
If both conditions match the device is turned off and the id is saved (if it is not excluded). The full code block for the check is:
local i = 0
local maxNodeID = 1000
for i = 0, maxNodeID do
if fibaro:getValue(i, "isLight") == "1" then
if (fibaro:getValue(i, "value") >= "1") then
local DeviceName = fibaro:getName(i)
local RoomName = fibaro:getRoomNameByDeviceID(i)
if not has_value(roomsWithLightsOn, RoomName) and RoomName ~= "unassigned" then
table.insert(roomsWithLightsOn, RoomName)
end
if RoomName ~= "unassigned" then
devicesOn = devicesOn .. i .. '|'
fibaro:debug(os.date("%a, %b %d") .. " Device is on: " .. i .. " " .. DeviceName .. " " .. RoomName)
-- If device if not on the exclusion list, turn it off
local excluded = has_value(exclusions, tostring(i))
if not has_value(exclusions, tostring(i)) then
fibaro:call(i , "turnOff")
else
fibaro:debug(os.date("%a, %b %d") .. " Excluded device: " .. i)
end
end
end
end
end
Check Philips Hue lights
If you have Philips Hue devices in your home and use the Fibaro Hue plug-in the Hue light sources are not detected with the code above. You can detect Hue devices with the following line of code:
if fibaro:getType(j) == "com.fibaro.philipsHueLight" then
...
To check if a Hue light is on you can use the following line of code:
if (fibaro:getValue(j, "on") == "1") then
...
For Hue devices I created another code block that also save the id’s from the lights that are on:
local j = 0
local maxHueID = 1000
for j = 0, maxHueID do
if fibaro:getType(j) == "com.fibaro.philipsHueLight" then
if (fibaro:getValue(j, "on") == "1") then
local DeviceName = fibaro:getName(j)
local RoomName = fibaro:getRoomNameByDeviceID(j)
if not has_value(roomsWithLightsOn, RoomName) and RoomName ~= "unassigned" then
table.insert(roomsWithLightsOn, RoomName)
end
devicesOn = devicesOn .. j .. '|'
fibaro:debug(os.date("%a, %b %d") .. " Hue device is on: " .. j .. " " .. DeviceName .. " " .. RoomName)
-- If Hue device if not on the exclusion list, turn it off
if not has_value(exclusions, tostring(j)) then
fibaro:call(j , "turnOff")
end
end
end
end
Saving the light state
The above scripts checked the lights that are still on in our house and turned them off (if they where not excluded). I saved a table with these lights and now I write this table to a global variable to use with the arrive home scene. With this info I can turn all lights back on in the same state when we left the house.
fibaro:setGlobal("SleepingLights", devicesOn:sub(1, -2)) -- remove last |
Scene 2: leave home explained
This scene is pretty simple. If both JoepPresent
and MoniquePresent
are set to No
the HomeState
variable is set to away
and the TurnOffAllLights scene is started. These variables are set with my presence detection scene.
if fibaro:getGlobalValue("JoepPresent") == "No" and fibaro:getGlobalValue("MoniquePresent") == "No" then
fibaro:setGlobal("HomeState", "away")
fibaro:debug(os.date("%a, %b %d") .. " Set HomeState to away.")
fibaro:setGlobal("TurnOffExclusions", "0") -- no exclusions
if fibaro:countScenes(33) < 1 then
fibaro:startScene(33) -- run Turn All Lights Off Scene
else
fibaro:debug(os.date("%a, %b %d") .. " Turn Of All Lights scene already running!")
end
end
Note: the TurnOffAllLights scene has id 33
in my system, you have to change that to the id on your own Home Center 2.
Scene 3: arrive home explained
The LUA scene to run when the first person arrive’s at home is a little bit more complex. At home I have tree entrances:
Entrance | Sensor |
---|---|
Front door | Fibaro Door/Window Sensor |
Garage door | Fibaro Motion Sensor |
Terrace door | Fibaro Motion Sensor |
As a house member can arrive at any of these doors I have to check all sensors for activity and if one sensor sends a breached state the Home Center 2 needs to act and start the arrive home scene.
Reading the sensors
With the following line I check all three sensors for activity:
if tonumber(fibaro:getValue(176, "value") > 0 or tonumber(fibaro:getValue(164, "value") > 0 or tonumber(fibaro:getValue(130, "value") > 0 then
...
Tackling the motion sensor alarm cancellation delay
The door sensor sets it’s value to 1
when I open the door, and sets it’s value back to 0
when I close the front door. The motion sensors keep their value at 1
until the alarm cancellation delay is reached (set with parameter 6).
This is a problem. When I enter the garage to get some stuff before leaving the house the value of the motion sensor stays 1
during the alarm cancellation delay, even if there is no motion anymore. If I grab something the garage and leave through the front door within this period the value of the motion sensor is still 1
and the lights go immediately back on when opening the front door. It took a while to figure this out!
I found out that the lastBreached
parameter keeps a timestamp value from the last time motion was detected. In the LUA scene I check if the active breach is within the alarm cancellation delay parameter value (6). In my case this is 5 minutes (or 300 seconds). If this is the case I report no movement and it doesn’t trigger the arrival scenario.
Another problem is that if the sensor is not breached the lastBreached value is 0
. The sensor is so fast the scene reads 0 as soon there is motion detected and it thinks there is no movement. I fix this behaviour with the following code:
local lastBreachedGarage = currentime - tonumber(fibaro:getValue(164, "lastBreached"))
local currentime = os.time(os.date("!*t"))
local noMovement = 1
if lastBreachedGarage > 2 and lastBreachedGarage < 310 then
noMovement = 0
end
Retrieving the light state and turn the lights back on when arriving home
If all these parameters are correct and the HomeState
global variable is away
I set the HomeState
back to athome
and check with a Fibaro motion sensor if the illuminance is below 10 to check if it’s dark and the light need to be turned back on:
if fibaro:getGlobalValue("HomeState") == "away" and noMovement == 1 then
fibaro:setGlobal("HomeState", "athome")
fibaro:debug(os.date("%a, %b %d") .. " Set HomeState to \"athome\".")
local currentLux = fibaro:getValue(160, "value")
if ( tonumber(currentLux) < 10 ) then
fibaro:debug(os.date("%a, %b %d") .. " Welcome home! Illuminance measuring " .. currentLux .. " lx, turn lights back on!")
...
If the illuminance is below 10 lux the Home Center 2 needs to know which lights to turn back on. I read the SleepingLights
global variable and put the id’s into a table:
local sleepinglights = fibaro:getGlobalValue("SleepingLights")
local lights = {}
for match in (sleepinglights..'|'):gmatch("(.-)"..'|') do
table.insert(lights, match);
end
Then I check the elapsed time between leaving and arriving back home. If it is less than 4 hours and there where lights left on, turn them back on:
local currentime = os.time(os.date("!*t"))
local sleepingtime = lights[1]
local elapsedtime = currentime - sleepingtime
if elapsedtime < 14400 and lights[2] ~= nill then -- if elapsed time is less than 4 hours.
fibaro:debug(os.date("%a, %b %d") .. " Last member of the family left less than 4 hours ago, return lights to previous state!")
for k, v in pairs(lights) do
if k ~= 1 then -- skip first, is unixtimestamp
fibaro:call(v , "turnOn")
fibaro:debug("turn on: " .. v)
end
else
...
If the elapsed time between leaving and arriving back home is more then 4 hours a standard arrival routine will run, because the previous light state is not important anymore. This routine you write in the else
statement of the previous if
condition:
...
else
fibaro:debug(os.date("%a, %b %d") .. " Last member of the family left more than 4 hours ago, start arrive home scene!")
fibaro:call(49, "setValue", "8") -- Spots garderobe
fibaro:call(172, "turnOn") -- Gurken garden
fibaro:call(140, "changeHue", "5021") -- Ledstip overloop
fibaro:call(140, "changeSaturation", "199") -- Ledstip overloop
fibaro:call(140, "changeBrightness", "114") -- Ledstip overloop
fibaro:call(140, "turnOn") -- Ledstip overloop
fibaro:call(44, "setValue", "8") -- Spots keuken
fibaro:call(39, "setValue", "35") -- Kookeiland
end
More awesome usecases
Because I made the TurnOffAllLights universal and supports exclusions you can make cool other scenes using it like a Go to Bed scene trigged by a scene activition. Also the arrive home scene can be edited to automatically create a nice cup of coffee when arriving home from a long day.
I’ll describe these cool features soon as I’m implementing these at the moment.
Download my scenes complete LUA code
You can download the full LUA scene code from here:
- Scene 1: TurnAllLightsOff.lua
- Scene 2: LeaveHome.lua
- Scene 3: ArriveHome.lua
You have to change the device id’s from my motion sensors in this scene to your own id’s!