Jump to content

Automatic Lightning System for HC3 (Motion/Average Lux)


Recommended Posts

 

Hi,

 

I want to share my automatic lightning system with the community. It controls my lights in a room based on motion sensors and average lux in the room. It’s build for HC3.

 

Goals:

-    I don’t want to use any buttons in this room, lights should turn on automatic by motions sensors
-    I want to measure the lux average and based on this value the lights turn on.
-    I want to have the ability to include exceptions, such as TV is on or Sonos is playing. In that case I don’t want the script to turn off the lights when there is no motion.
-    I want different light settings for day and night. I also want to reuse these light settings and don’t want it to integrate it into the motion script.
-    I want to reuse the scripts easily for other rooms.

 

Good to know:

-    The basic of the motion code is from SmartHomeEddy 
-    I was also inspired by this article (but it's build for HC2) 
-    I bought a HC3 couple of weeks ago and I am totally new into LUA.
-    I am not a professional software engineer. I am sure my code is not the best and can be improved. Feel free.
-    I post this information for beginners and amateurs like me. Spent a lot of time finding the correct information because most of the scripts are created for HC2 and is not always easy to migrate to HC3.
-   Below a step by step guide to achieve above goals.
 

Step 1: Measure the average lux in the room

-    Create a global variable. In my script “luxAverageKitchen”.
-    Make sure to change Fibaro Motion Sensor parameter 40 (set it to 5lux) and parameter 42 (set it to 900s) of all the motions sensors that you want to use for calculating the average lux in the room. In my script I use sensor 69 and 38.

- Make sure to set this script to automatic.

 

Calculate average lux scene

 

Triggers

{   
        
            --Triggers for the lux sensors
            operator = "any",
            conditions = {
                {
                     --Lux sensor 1
                    id = 69,
                    isTrigger = true,
                    operator = "anyValue",
                    property = "value",
                    type = "device",
                    value = true
                },
                {
                    --Lux sensor 2
                    id = 38,
                    isTrigger = true,
                    operator = "anyValue",
                    property = "value",
                    type = "device",
                    value = true
                
            }
 
}
}

Actions

 

local lux = {69,38} -- ID's of the Lux sensors
local debug_TAG = "Kitchen Lux" -- Tag for the debug messages
local luxRoom = "luxAverageKitchen" --Defines the name of the global variable to store the lux of the room.


-- CALCULATE AVERAGE LUX IN THE ROOM AND SAVE TO GLOBAL VARIABLE
function GetAverageLuxValue()
	
    -- ROUND NUMBERS FUNCTION USED TO ROUND LUX AVERAGE
	function roundit(num, idp)
  		local mult = 10^(idp or 0)
		return math.floor(num * mult + 0.5) / mult
	end
    local luxAverage = 0;
    for i = 1,#lux do 
        luxAverage = luxAverage + tonumber(fibaro.getValue(lux[i], "value"));
    end
    luxAverage = roundit( (luxAverage / #lux), 0 );				
    return luxAverage;
end

-- SHOW LUX LEVEL OF EACH LUX SENSOR
for i in pairs(lux) do -- Show the lux level of the Lux sensors
  fibaro.debug(debug_TAG,"Current Lux level " ..lux[i] .." " ..fibaro.getName(lux[i]) ..": " ..fibaro.getValue(lux[i],"value"))  
end

-- SHOW AVERAGE LUX IN THE ROOM
fibaro.debug(debug_TAG,"Average Lux in room: " .. GetAverageLuxValue())
local luxAverageRoom = GetAverageLuxValue()
fibaro.setGlobalVariable("" .. luxRoom, "" .. luxAverageRoom)

 

 

Step 2: Set TV Status

In the motion script we need the TV status. I use Fibaro wall plugs for my TV in the living room and the TV in the bedroom.

-    Create a global variable for each TV (tvLivingStatus and tvBedroomStatus in my script)

-    Device ID 44 is my TV in the living room and is used in the motion script.

- Make sure to set this script to automatic.

 

Set TV Status scene

 

Triggers


{    
            --TRIGGERS/CONDITIONS FOR TV POWER USAGE
            operator = "any",
            conditions = {
                {
                	 --TV LIVING ROOM
				    id = 44,
   					isTrigger = true,
  				    operator = "anyValue",
      				property = "power",
      				type = "device",
                },
                {
					-- TV BEDROOM
					id = 46,
   					isTrigger = true,
  				    operator = "anyValue",
      				property = "power",
      				type = "device",
                }
            }
}

Actions:

 

-- ID's OF WALL PLUGS CONNECTED TO TV
local tvLivingroomID = 44 
local tvBedroomID = 46

-- GLOBAL VARIABLES OF THE TV's
local tvLivingroomGB = "tvLivingStatus"
local tvBedroom = "tvBedroomStatus"


-- LIVING ROOM
local tvLivingroomWatt = tonumber(fibaro.getValue(tvLivingroomID, 'power'))
if (tvLivingroomWatt > 50) then
	fibaro.setGlobalVariable('tvLivingStatus', 'on')
end
if (tvLivingroomWatt < 50) then
	fibaro.setGlobalVariable('tvLivingStatus', 'off')
end

-- BEDROOM
local tvBedroomWatt = tonumber(fibaro.getValue(tvBedroomID, 'power'))
if (tvBedroomWatt > 50) then
	fibaro.setGlobalVariable('tvBedroomStatus', 'on')
end
if (tvBedroomWatt < 50) then
	fibaro.setGlobalVariable('tvBedroomStatus', 'off')
end

Step 3: Motion script
- Now finally the script that runs after motion detection. Please make sure that you fill in the correct global variables and id’s.

- Make sure to set this script to automatic.

 

Motion Scene:

 

Triggers

 

{    
            --TRIGGERS/CONDITIONS FOR THE MOTIONS SENSORS IN THE ROOM
            operator = "any",
            conditions = {
                {
                     --MOTION SENSOR 1 (LIVING))
                    id = 72,
                    isTrigger = true,
                    operator = "==",
                    property = "value",
                    type = "device",
                    value = true
                },
                {
                     --MOTION SENSOR 2 (LIVING))
                    id = 67,
                    isTrigger = true,
                    operator = "==",
                    property = "value",
                    type = "device",
                    value = true
                },
                
                {
                    --MOTION SENSOR 3 (KITCHEN)
                    id = 36,
                    isTrigger = true,
                    operator = "==",
                    property = "value",
                    type = "device",
                    value = true
                }
            }
}

Actions

 

-- This scene turns on one or more lights: 
--    It receives the lux average of the room from a global variable
--    It turns on the lights when there is motion only when the lux average is below the minLux value
--    It turns off the lights after a x time when there is no motion unless the tv is on.
--    In this example my light is a binary sensor (via quick app, device id 65) that turns on multiple lights.


-- Please enter the right ID's (if more than one, seperate by ",") and the prefered maxTime for the lights to stay on 
local light = {65} -- ID's of the lights to turn on. I only use one light which is a Quick app managing all the lights in my room
local motion = {67,72,36} -- ID's of the Motion sensors in the room
local maxTime = 10*60  -- Maximum time the sensor should be safe before turning off (n*60 where n is minutes)
local sleepTime = 1 -- Time in seconds between each check of the Motion sensor (default = 5)
local debug_TAG = "Ground floor lights" -- Tag for the debug messages
local luxRoom = "luxAverageKitchen" --The name of the global variable to get the lux of the room.
local minLux = 150 -- When the lux is less then this value, the lights are turned on.
local tvStatus = "tvLivingStatus" --The name of the global variable to get the status of the TV (on/off)

------ FUNCTIONS---------------------
-------------------------------------

function turnOnLights()
	-- Get average lux of the room
	local localLuxAverageRoom = (tonumber(fibaro.getGlobalVariable(luxRoom)))
	fibaro.debug(debug_TAG,"Average Lux in the room : " ..localLuxAverageRoom)

	-- IF LUX IS UNDER MINLUX, TURN LIGHTS ON. IF LUX IS ABOVE MINLUX, DO NOT TURN ON
	for i in pairs(light) do 
		if (localLuxAverageRoom < minLux) then
			-- turn on the lights
			fibaro.debug(debug_TAG,"Turning on (ID " ..light[i] ..") " ..fibaro.getName(light[i]) .." for " ..maxTime .." seconds")
  			fibaro.call(light[i],"turnOn")
		end
		if (localLuxAverageRoom > minLux) then
			-- do not turn on
			fibaro.debug(debug_TAG,"To bright to turn the lights on.") 
		end
	end
end


function safeToTurnOff()

	-- CHECK TV STATUS FROM GLOBAL VARIABLE
	local localTvStatus = fibaro.getGlobalVariable(tvStatus)
	if (localTvStatus == 'on') then
		return false
	else
		return true
	end
	
end

function countDown()
	local safeTime = 0
	while safeTime < maxTime do -- Loop until maxTime is reached
		fibaro.sleep(sleepTime*1000)                 
		safeTime=safeTime+sleepTime 
  		fibaro.debug(debug_TAG,"Counting up safeTime " ..safeTime .." to maxTime " ..maxTime)
		for i in pairs(motion) do 
    		if fibaro.getValue(motion[i],"value") then -- Check if Motion sensors are breached
       		-- Uncomment next line so motion is detected in the debug screen.
       		fibaro.debug(debug_TAG,"Reset by Motion sensor " ..motion[i] .." " ..fibaro.getName(motion[i]))
       		safeTime = 0 -- Reset safeTime to zero
    		end
  		end
	end 
	-- Check if it's safe to turn off
	safeToTurnOff()
end
  
function turnOffLights()
	for i in pairs(light) do -- Counted down to maxTime, turn off the lights
 	 fibaro.debug(debug_TAG, "Turning off " ..light[i] .." " ..fibaro.getName(light[i]))
 	 fibaro.call(light[i],"turnOff") 
	end
end

-----------LOGIC---------------------
-------------------------------------

-- MOTION IS DETECTED, TURN ON THE LIGHTS
turnOnLights()

-- START TIMER WHEN THE LIGHTS ARE ON
countDown()

-- NO MOTION AND COUNTDOWN IS DONE

-- RESET TIMER IN CASE TV IS ON
while (safeToTurnOff() == false) do
	fibaro.debug(debug_TAG,"Not safe to turn off the lights, reset timer.")
	-- RESTART TIMER
	countDown()
end
	
-- TURN OFF THE LIGHTS
turnOffLights()

 

Step 4: Manage all room lights from 1 Quick app (optional)

 

-   As you can see I only use 1 light in the motion script, id 65 refers to a quick app managing all the lights of the ground floor.

-  This way I can easly switch on all lights using Google home, the app or maybe in the future a button.
-    Make sure to create a global variable for the Day/Night settings (from 06:00 till 02:00 Day is set, from 02:00 till 06:00 Night is set in the script). In my script the global variable it is called “SetTimeOfDayLights” and only has two variables: Day or Night.
-    Below you can find the scripts.

 

SetTimeOfDayLights scene

 

Triggers

 

{    
            --TRIGGERS FOR SPECIFIC TIMES
            operator = "any",
            conditions = {
                {
                     --06:00 TRIGGER
                    isTrigger = true,
                    operator = "match",
                    property = "cron",
                    type = "date",
                    value = { "00", "06", "*", "*", "*", "*" }
                },
                {
                    --01:00 TRIGGER
                    isTrigger = true,
                    operator = "match",
                    property = "cron",
                    type = "date",
                    value = { "00", "01", "*", "*", "*", "*" }
                }
            }
}

Actions

 

local debug_TAG = "DAY/NIGHT SETTINGS FOR LIGHTS" -- Tag for the debug messages
local currentTime = os.date("%H:%M")

--IF 06:00, THEN SET DAY
if (currentTime == "06:00") then
    fibaro.debug(debug_TAG,"" .. currentTime ..": DAY settings activated")
    fibaro.setGlobalVariable('SetTimeOfDayLights', 'Day')
end

--IF 01:00, THEN SET NIGHT
if (currentTime == "01:00") then
    fibaro.debug(debug_TAG,"" .. currentTime ..": NIGHT settings activated")
    fibaro.setGlobalVariable('SetTimeOfDayLights', 'Night')
end

 

Quick app (Lights switch for whole room)

-    Create a quick app by using the Binary switch template.

- Go to the device --> Edit & Preview --> Edit

- Paste below code.

 

-- Binary switch type should handle actions turnOn, turnOff
-- To update binary switch state, update property "value" with boolean

-- Function that triggers the correct light settings based on the global variable
function QuickApp:DetermineSettings()
    local localSetTimeOfDayLights = fibaro.getGlobalVariable("SetTimeOfDayLights")
    if localSetTimeOfDayLights == "Day" then
        self:DaySettings()
    end

    if localSetTimeOfDayLights == "Night" then
        self:NightSettings()
    end
end

-- Light settings when it's Day
function QuickApp:DaySettings()
	-- Spot 1
    fibaro.call(31, 'changeBrightness', 150)
	
	-- Spot 2
    fibaro.call(32, 'changeBrightness', 150)
    
    -- Spot 3
    fibaro.call(27, 'changeBrightness', 150)
    
    -- Spot 4
    fibaro.call(26, 'changeBrightness', 150)
    
	-- Hue Color TV
    fibaro.call(29, 'changeBrightness', 255)
    fibaro.call(29, 'changeHue', 6881)
    fibaro.call(29, "changeSaturation", 255)
    
    -- Hue Iris
    fibaro.call(28, 'changeBrightness', 200)
    fibaro.call(28, "changeHue", 6881)
    fibaro.call(28, "changeSaturation", 255)
    
    -- Hue Color Bank
    fibaro.call(23, 'changeBrightness', 150)
    fibaro.call(23, "changeHue", 6881)
    fibaro.call(23, "changeSaturation", 255)
    self:debug("Light settings actived: DAY")
end

-- Light settings when it's Night
function QuickApp:NightSettings()

	-- Spot 1
    fibaro.call(31, 'changeBrightness', 20)
	
	-- Spot 2
    fibaro.call(32, 'changeBrightness', 20)
    
    -- Spot 3
    fibaro.call(27, 'changeBrightness', 20)
    
    -- Spot 4
    fibaro.call(26, 'changeBrightness', 20)
    
	-- Hue Color TV
    fibaro.call(29, 'changeBrightness', 20)
    fibaro.call(29, 'changeHue', 6881)
    
    -- Hue Color Iris
    fibaro.call(28, 'changeBrightness', 20)
    fibaro.call(28, "changeHue", 6881)

    -- Hue Bank
    fibaro.call(23, 'changeBrightness', 20)
    fibaro.call(28, "changeHue", 6881)

    self:debug("Light settings actived: NIGHT")

end

-- Turn ON all the lights
function QuickApp:turnOn()
    self:updateProperty("value", true)
    -- Spot 1
    fibaro.call(31, 'turnOn')
    -- Spot 2
    fibaro.call(32, 'turnOn')
    -- Spot 3
    fibaro.call(27, 'turnOn')
    -- Spot 4
    fibaro.call(26, 'turnOn')
    -- Hue Color TV
    fibaro.call(29, 'turnOn')
    -- Hue Iris
    fibaro.call(28, 'turnOn')
    -- Hue Color Bank
    fibaro.call(23, 'turnOn')
    self:DetermineSettings()
    self:debug("Lights ground floor turned ON")
end

-- Turn OFF all the lights
function QuickApp:turnOff()
    self:updateProperty("value", false) 
    -- Spot 1
    fibaro.call(31, 'turnOff')
    -- Spot 2
    fibaro.call(32, 'turnOff')
    -- Spot 3
    fibaro.call(27, 'turnOff')
    -- Spot 4
    fibaro.call(26, 'turnOff')
    -- Hue Color TV
    fibaro.call(29, 'turnOff')
    -- Hue Iris
    fibaro.call(28, 'turnOff')
    -- Hue Color Bank
    fibaro.call(23, 'turnOff')
    self:debug("Lights ground floor turned OFF")
end

function QuickApp:onInit()
        
end

 

 All these scripts are working great on my HC3 (5.040.37) . But in my log file I see (sometimes) an error is occurring in the motion scene without any information in the debug line. If someone knows what it is, let me know ;-)


That's it. Enjoy! 

 

 


 

 

 

Edited by chiel-s
  • Thanks 1
Link to post
Share on other sites
  • Replies 54
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

See example below. I use global variable for my TV and my Sonos in the kitchen. Make sure to use 0 (off) and 1 (on) as value for the gobal variables.   Keuken = { name = "Keuken", sen

Hi,   I want to share my automatic lightning system with the community. It controls my lights in a room based on motion sensors and average lux in the room. It’s build for HC3.   G

Thanks!! What a great script  It is working! Now I get it, you can combine the TV as a "motion sensor" in the room configuraton. Brilliant.   Keuken = { name = "Keuken", sensors = {67

Posted Images

TBh I hate Fibaro condition rules ... in HC2 it was suxxx in HC3 more pretty but still as for me suzpxx also 

so, I have script for motion lights what do not used any conditions (loops events) , works for all rooms in my home, have exclusion by other devices, have time rules and you can override any conditions by time slot for example min lux at the motion 100 and 50at the evening or set dim event evening 50 and 1 at the night 

can keep off state with pre defined dim level (so if no motion my led strip for example has level 1 instead of full off...)

etc 

 

Link to post
Share on other sites
49 minutes ago, 10der said:

TBh I hate Fibaro condition rules ... in HC2 it was suxxx in HC3 more pretty but still as for me suzpxx also 

so, I have script for motion lights what do not used any conditions (loops events) , works for all rooms in my home, have exclusion by other devices, have time rules and you can override any conditions by time slot for example min lux at the motion 100 and 50at the evening or set dim event evening 50 and 1 at the night 

can keep off state with pre defined dim level (so if no motion my led strip for example has level 1 instead of full off...)

etc 

 

 

That's the difference between a newbie and an expert ☺️

Link to post
Share on other sites

just want to share my code

 

 

 

 

NB: in header of this message - samples. actually  I have ~20 ligthing devices controlled by this script

Edited by 10der
Link to post
Share on other sites
  • 1 month later...
On 8/4/2020 at 8:34 AM, 10der said:

just want to share my code

 

NB: in header of this message - samples. actually  I have ~20 ligthing devices controlled by this script

 

 

 

Great script @10der! It is working :-) I added the script to a Quick App using the Multilevel sensor as template, was that correct?

I got a question. I don't want to turn off the light in a room when my TV is on. How is that possible in your script? Could you help me out? I got a global variable that determines the status of my TV. Does your script support this function by default? Else I will try to integrate it myself.

 

 

 

 

 

Edited by chiel-s
Link to post
Share on other sites
51 minutes ago, chiel-s said:

sing the Multilevel sensor as template, was that correct?

I use general. I do not want use controls on the face.

 

52 minutes ago, chiel-s said:

I got a global variable that determines the status of my TV. Does your script support this function by default? Else I will try to integrate it myself.

yes. you can.

 

just make minor fix. 

in HC3 i have disable it by default,

1 minute!

 

"Motion_Lights (4).fqa" 

fixed

 

local lighting = {
    Test = {
        name = "Test Test ",
        sensors = {"Test1"},
        luxs = {},
        powers = {989},
        timeout = 60 * 1,
        times = {
            ["$Sunset..$SunriseTwilight"] = {
                timeout_manual = 60 * 1
            }
        }
    }
}

Test1 - global var 

can be 0..99 as dimmer

0 / 1 like switch

Link to post
Share on other sites
30 minutes ago, 10der said:

I use general. I do not want use controls on the face.

 

yes. you can.

 

just make minor fix. 

in HC3 i have disable it by default,

1 minute!

 

"Motion_Lights (4).fqa" 

fixed

 

local lighting = {
    Test = {
        name = "Test Test ",
        sensors = {"Test1"},
        luxs = {},
        powers = {989},
        timeout = 60 * 1,
        times = {
            ["$Sunset..$SunriseTwilight"] = {
                timeout_manual = 60 * 1
            }
        }
    }
}

Test1 - global var 

can be 0..99 as dimmer

0 / 1 like switch

 

Thanks for your quick reply! I have installed the new script and it's up and running.

I tested it with a global variable (value is 0 or 1), but I get the following error:

 

[06.09.2020] [21:44:02] [DEBUG] [QA_MOTION_LIGHTS_112]: ERROR: TV living room ERR: ./include/main.lua:472: attempt to compare number with nil

I added:

 

tvLivingRoom = {
	name = "TV living room",
    sensors = {"statusTvLivingRoom1"},
    luxs = {},
    powers = {65},
    timeout = 60 * 1,
    times = {
            	["$Sunset..$SunriseTwilight"] = {
                timeout_manual = 60 * 1 }
        	}
    }

 

Link to post
Share on other sites

@chiel-s Yes, cuz 

SunriseTwilight

unknown for you.

just change to

 

local lighting = {
    Test = {
        name = "Test Test ",
        sensors = {"Test1"},
        luxs = {},
        powers = {989},
        timeout = 60 * 1,
        times = {
            ["$Sunset..$Sunrise"] = {
                timeout_manual = 60 * 1
            }
        }
    }
}

or remove ovirrede condition at all

 

local lighting = {
    Test = {
        name = "Test Test ",
        sensors = {"Test1"},
        luxs = {},
        powers = {989},
        timeout = 60 * 1,
    }
}
Link to post
Share on other sites

 

Thanks!! What a great script :-) It is working! Now I get it, you can combine the TV as a "motion sensor" in the room configuraton. Brilliant.

 

Keuken = {
    name = "Keuken",
    sensors = {67,72,36,"statusTvLivingRoom1"},
    luxs = {69,38},
    minLux = 120,
    powers = {65},
    timeout = 5
  },

 

Edited by chiel-s
  • Thanks 1
Link to post
Share on other sites
8 hours ago, jorge rintaro said:

[email protected],

forgive me for the stupid question, but how do I open your code for viewing (.fqa)? thankful

 

See the post from @10der on the 4th of august :-)

 

 

  

On 8/4/2020 at 8:34 AM, 10der said:

just want to share my code

 

 

Motion_Lights (4).fqa 35.53 kB · 6 downloads

 

NB: in header of this message - samples. actually  I have ~20 ligthing devices controlled by this script

 

Link to post
Share on other sites
On 8/4/2020 at 8:34 AM, 10der said:

just want to share my code

 

 

Motion_Lights (4).fqa 35.53 kB · 7 downloads

Hi @10der, Just uploaded your QA to my HC3, but getting error:

07.09.2020] [12:21:00] [ERROR] [QUICKAPP623]: QuickApp crashed[07.09.2020] [12:21:00] [ERROR] [QUICKAPP623]: /usr/share/lua/5.3/json/decode.lua:74: bad argument #1 to 'match' (string expected, got nil)

Any ideas?

line 74:

 

times = {
      ["$SunriseTwilight..$NightTime"] = {timeout = 60 * 30},["$NightTime..$SunriseTwilight"] = {timeout = 60 * 5}
    }
Edited by Bodyart
Link to post
Share on other sites

to @Bodyart and others

if you wnat to use the custom var in times?

for example

["$SunriseTwilight..$NightTime"] 

["$SunriseTwilight..$SleepTime"] 

["$SunriseTwilight..$EatTime"] 

 

etc please define this var in globals,

lxNQfoicir.png

 

 

 

  • Thanks 1
Link to post
Share on other sites

Thanks for fast response :-D I think i will put those definition to Quickvars :-)

 

Link to post
Share on other sites

it's vars may used all QA's or scenas - so this vars should be in Global vars.

alas.

Link to post
Share on other sites

PS: If you used some additional vars from sun calculation do not forget import my QA with 

laYPNJWxwU.png

 

ths QA alos update globalvars with actial values.

 

@Bodyart

  • Thanks 1
Link to post
Share on other sites
14 minutes ago, 10der said:

hs QA alos update globalvars with actial values.

I will add also creation of globals :-D otherwise one must add them manually....

 

Link to post
Share on other sites

@10der, is it possible to configure settings for hue lights? Such as:

 

fibaro.call(29, 'changeBrightness', 255)
fibaro.call(29, 'changeHue', 6881)
fibaro.call(29, "changeSaturation", 255)

Would be great. The last piece to finalize my light automation :-) I have tried 'level' but that seems not be working for Hue lights.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...