Jump to content

HC3 Scenes - schedule actions on times


Recommended Posts

When doing a scene on the HC3 there is the option to schedule invocations of the scene using the conditions sections and the 'cron' property.

{
  conditions = { 
  {
    type = "date",
    property = "cron",
    operator = "match",
    value = {"*", "*", "*", "*", "*", "*"},
    isTrigger = true
   }
 },
  operator = "all"
}

The above condition will trigger the scene every minute.

 

There are possibilities to be more specific and only trigger every minute on weekends with the value {"*", "*", "*", "*", "6,7", "*"}. etc.. 

It's also possible to limit it between sunset and sunrise but then we need to make more elaborate conditions. If we want to cover many different times we either need to create a very long condition (with any) and in the scene test what time was triggered - or create many smaller scenes that may turn out difficult to maintain.

 

So, if you want to keep most of your time scheduling in one scene, one approach is to setup a trigger for every minute like the condition above - and then in the scene make tests if the time is right for your various actions

 

One approach to structure such as scene is like below. It relies on being triggered every minute - and calling a user provided function clock(....) with various "nice to have parameters" that can be easily tested against if the time is right to carry out some actions.

dayMap={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}

local lamp = 114
local motion = 117

-- The function 'clock' is called every minute, define your tests and actions inside the function
--- Variable 'time' is the current time as a string, ex. "17:42"
--- Variable 'sunset' is today's sunset time as a string ex. "21:33"
--- Variable 'sunrise' is today's sunrise time as a string ex. "06:35"
--- Variable 'day' is name of day ex. "Monday"
--- Variable 'weekend' is true if it's weekend (Saturday,Sunday)
function clock(time,sunrise,sunset,day,weekend) -- called every minute, on the minute

  print("Time:"..time)

  -- Add your Lua tests here
  
  if time=="21:31" and fibaro.getGlobal("Home_Away")=="Away" then -- at 21:31 if global variable is set to 'Away'
    fibaro.call(lamp,"turnOn")
  end

  if time=="21:45" and day=="Monday" then -- at 21:45 if it's Monday
    fibaro.call(lamp,"turnOff")
  end
  
  if time=="21:00" and weekend then -- at 21:00 on weekends
    fibaro.call(lamp,"turnOff")
  end

  if time==sunrise and not weekend then  -- at sunrise on weekdays
    fibaro.call(lamp,"turnOff")
  end
  
  if time>sunrise and time<sunset and time:match("00$") then -- every hour between sunrise and sunset
    fibaro.call(lamp,"turnOff")
  end
  
  if (time>sunset or time<sunrise) and time:match("00$") then  -- every hour between sunset and sunrise
    fibaro.call(lamp,"turnOff")
  end

  if tonumber(time)==tonumber(sunrise)-tonumber("00:10") and weekend then -- at 10min before sunrise on weekend
    fibaro.call(lamp,"turnOff")
  end
  
  if time>sunrise and time<sunset and       -- Between sunrise and sunset
    fibaro.getValue(lamp,"state") and       -- and lamp is on
    not fibaro.getValue(motion,"state") and -- and motion sensor is safe
    lastChanged(motion,'state') > 5*60      -- and the last time the motion sensor changed state was (more than) 5min ago
  then  
     fibaro.call(lamp,"turnOff")            -- then turn off lamp
  end   

end

--------- Helper functions, don't touch----------------
do
  function lastChanged(id,prop) return os.time()-select(2,fibaro.get(id,prop)) end
  tonumber,oldTonumber=function(str) 
    local h,m,s=str:match("(%d%d):(%d%d):?(%d*)")
    return h and m and h*3600+m*60+(s~="" and s or 0) or oldTonumber(str)
  end,tonumber

  local d = os.date("*t").wday
  local ss,sr = fibaro.getValue(1,"sunsetHour"),fibaro.getValue(1,"sunriseHour")
  clock(os.date("%H:%M"),sr,ss,dayMap[d],d==1 or d==7)
end

We can reason about time as a Lua string of format "HH:MM". We can test for equality and less than and greater than (be aware of times spanning midnight). We have also boosted the tonumber() function so that it converts a "HH:MM" string to it's value in seconds. That allows us to do some simple arithmetic like in the last example where we test if the time is 10min before sunrise and it's weekend.

Running a scene like this every minute will not take a huge toll on the HC3...

 

Anyway, this is a simple way to create a scheduler of actions using a scene - I'm sure there will be more to come...

Edited by jgab
  • Thanks 2
Link to post
Share on other sites
  • 4 weeks later...
  • 1 month later...
On 3/23/2020 at 5:06 PM, jgab said:

When doing a scene on the HC3 there is the option to schedule invocations of the scene using the conditions sections and the 'cron' property.

{
  conditions = { 
  {
    type = "date",
    property = "cron",
    operator = "match",
    value = {"*", "*", "*", "*", "*", "*"},
    isTrigger = true
   }
 },
  operator = "all"
}

The above condition will trigger the scene every minute.

 

There are possibilities to be more specific and only trigger every minute on weekends with the value {"*", "*", "*", "*", "6,7", "*"}. etc.. 

It's also possible to limit it between sunset and sunrise but then we need to make more elaborate conditions. If we want to cover many different times we either need to create a very long condition (with any) and in the scene test what time was triggered - or create many smaller scenes that may turn out difficult to maintain.

 

So, if you want to keep most of your time scheduling in one scene, one approach is to setup a trigger for every minute like the condition above - and then in the scene make tests if the time is right for your various actions

 

One approach to structure such as scene is like below. It relies on being triggered every minute - and calling a user provided function clock(....) with various "nice to have parameters" that can be easily tested against if the time is right to carry out some actions.

dayMap={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}

local lamp = 114
local motion = 117

-- The function 'clock' is called every minute, define your tests and actions inside the function
--- Variable 'time' is the current time as a string, ex. "17:42"
--- Variable 'sunset' is today's sunset time as a string ex. "21:33"
--- Variable 'sunrise' is today's sunrise time as a string ex. "06:35"
--- Variable 'day' is name of day ex. "Monday"
--- Variable 'weekend' is true if it's weekend (Saturday,Sunday)
function clock(time,sunrise,sunset,day,weekend) -- called every minute, on the minute

  print("Time:"..time)

  -- Add your Lua tests here
  
  if time=="21:31" and fibaro.getGlobal("Home_Away")=="Away" then -- at 21:31 if global variable is set to 'Away'
    fibaro.call(lamp,"turnOn")
  end

  if time=="21:45" and day=="Monday" then -- at 21:45 if it's Monday
    fibaro.call(lamp,"turnOff")
  end
  
  if time=="21:00" and weekend then -- at 21:00 on weekends
    fibaro.call(lamp,"turnOff")
  end

  if time==sunrise and not weekend then  -- at sunrise on weekdays
    fibaro.call(lamp,"turnOff")
  end
  
  if time>sunrise and time<sunset and time:match("00$") then -- every hour between sunrise and sunset
    fibaro.call(lamp,"turnOff")
  end
  
  if (time>sunset or time<sunrise) and time:match("00$") then  -- every hour between sunset and sunrise
    fibaro.call(lamp,"turnOff")
  end

  if tonumber(time)==tonumber(sunrise)-tonumber("00:10") and weekend then -- at 10min before sunrise on weekend
    fibaro.call(lamp,"turnOff")
  end
  
  if time>sunrise and time<sunset and       -- Between sunrise and sunset
    fibaro.getValue(lamp,"state") and       -- and lamp is on
    not fibaro.getValue(motion,"state") and -- and motion sensor is safe
    lastChanged(motion,'state') > 5*60      -- and the last time the motion sensor changed state was (more than) 5min ago
  then  
     fibaro.call(lamp,"turnOff")            -- then turn off lamp
  end   

end

--------- Helper functions, don't touch----------------
do
  function lastChanged(id,prop) return os.time()-select(2,fibaro.get(id,prop)) end
  tonumber,oldTonumber=function(str) 
    local h,m,s=str:match("(%d%d):(%d%d):?(%d*)")
    return h and m and h*3600+m*60+(s~="" and s or 0) or oldTonumber(str)
  end,tonumber

  local d = os.date("*t").wday
  local ss,sr = fibaro.getValue(1,"sunsetHour"),fibaro.getValue(1,"sunriseHour")
  clock(os.date("%H:%M"),sr,ss,dayMap[d],d==1 or d==7)
end

We can reason about time as a Lua string of format "HH:MM". We can test for equality and less than and greater than (be aware of times spanning midnight). We have also boosted the tonumber() function so that it converts a "HH:MM" string to it's value in seconds. That allows us to do some simple arithmetic like in the last example where we test if the time is 10min before sunrise and it's weekend.

Running a scene like this every minute will not take a huge toll on the HC3...

 

Anyway, this is a simple way to create a scheduler of actions using a scene - I'm sure there will be more to come...

 

 

 

hello @jgab

 

just a quick and stupid question... or two... :)

 

* how to do when you want a condition running the scene every 2 minutes ? every 5 ...

* how to do to launch scene every hours at the 17th and 43rd minutes of it ?

 

struggling with this :)

thanks in advance,

 

br

mde

 

 

 

 

 

 

 

 

 

 

 

Link to post
Share on other sites
Posted (edited)
5 hours ago, Mateo said:

hello @jgab

just a quick and stupid question... or two... :)

* how to do when you want a condition running the scene every 2 minutes ? every 5 ...

* how to do to launch scene every hours at the 17th and 43rd minutes of it ?

struggling with this :)

thanks in advance,

br

mde

Well, there are a couple of ways to do that.

First to recognise that the 'time' parameter is in the format "HH:MM".

You can extract the minute part with time:sub(4)

local minute = time:sub(4)
if minute=="17" or minute=="43" then 
    ....
end

You can also match against a string pattern

if time:match(":17") or time:match(":43") then 
    ....
end

If you want to match against every nth minute there are some other approaches

Our 'tonumber' converts the time string to seconds. You can then test if the seconds are evenly divided with the minute interval you look for

n = 2
if tonumber(time) % n*60 == 0 then 
    ....
end

n = 5
if tonumber(time) % n*60 == 0 then 
    ....
end

This will run 00:02, 00:04 etc. If you don't care if it starts on an even interval, e.g. 00:01,00:03,.. is ok you can keep your own minute counter. The clock function is called every minute so you can add that counter as a variable

local minutes = 0 

function clock(time,sunrise,sunset,day,weekend) -- called every minute, on the minute

  print("Time:"..time)

  if minutes % 2 == 0 then
      ...
  end

  if minutes % 5 == 0 then
      ...
  end

  minutes = minutes+1
end

A general way to test against different minute intervals is to check if the minute part of the string exists within a larger string of minutes that you want to match.

Ex. if current minute part "17" exists in the string "09 17 23 43 59", which in this case it does

local minutePattern = "09 17 23 43 59"
if minutePattern:match(time:sub(4)) then -- will match HH:09, HH:17, HH:23, HH:43, HH:59
      ....
end

 

Edited by jgab
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...