Jump to content

Welcome to Smart Home Forum by FIBARO

Dear Guest,

 

as you can notice parts of Smart Home Forum by FIBARO is not available for you. You have to register in order to view all content and post in our community. Don't worry! Registration is a simple free process that requires minimal information for you to sign up. Become a part of of Smart Home Forum by FIBARO by creating an account.

 

As a member you can:

  •     Start new topics and reply to others
  •     Follow topics and users to get email updates
  •     Get your own profile page and make new friends
  •     Send personal messages
  •     ... and learn a lot about our system!

 

Regards,

Smart Home Forum by FIBARO Team


   (5 reviews)

About This File

This is a scene that runs in the background and schedules your scenes that have a '%% timer' included

(the original thread for this scene is <here>)

 

Scenes can trigger on devices changing status (%% properties), when globals change values (%% globals) and events (%% events) etc.

However, there is no trigger for time or timers - to allow a scene to be started at a specified time.

Well, here is a fix for that... and it makes it very easy to start scenes at specified times of day including sunrise/sunset, at regular intervals, at specified weekdays, and at specified months...

 

The scene below watches other scenes and allows them to declare "%% time" headers that they will be triggered on

 

When the Timer.lua scene is installed and started (no configuration needed) we should be able to forget about it and turn our attention to scenes that we want to be triggered at given times.

It's a perfect "tool" to have running on the HC2 as it makes it really easy to add a scene and schedule it to run at a given time. A simple added header to the scene is all that is needed:

--[[
%% properties
%% events
%% globals
%% time
15:00
--]] 

print("Scene started at 15:00")

This scene will be run at 15:00 every day. To stop it from being scheduled, just remove the "%% time" lines from the header and save the scene again.

 

A more extensive example with a scene declaring multiple triggers and retrieving the trigger when the scene gets invoked;

--[[
%% properties
%% events
%% globals
%% time
log
15:00 bar -- scene triggered at 15:00 every day
*00:15 test -- scene triggered every 15min throughout the day
--]] 

print("Scene started")

-- Redefine fibaro:getSourceTrigger
do local a,b=fibaro;b=a.getSourceTrigger;function a:getSourceTrigger()local c=b(a)local d=a:args()if type(d)=='table'and d[1]and type(d[1])=='table'then if d[1].type~= nil then return d[1]end end;return c end end

local st=fibaro:getSourceTrigger()

if st.type=='time' and st.time then -- do something when we get a time trigger...
  fibaro:debug(string.format("Triggered:%s, tag:'%s'",st.time,st.tag))
end

if st.type=='time' and st.tag=='log' then -- write out log messages from the time scene
  fibaro:debug("Time log:")
  fibaro:debug(st.log) 
end 

if st.type=='time' and st.tag=='error' then -- write out error messages from the time scene
  fibaro:debug("Time error:"..st.log)
end 

In the scene we also redefine fibaro:getSourceTrigger() to return our timer as an "standard" source trigger. It's not strictly necessary as time triggers are of type 'other' with the arguments coming from fibaro:args(). However, this makes it more streamlined and plays well with standard fibaro source triggers. The timer scene is "drift free" so if you declare a timer on the hour you will be called exactly on the hour, and not slowly start to drift as is very common in many home made Lua timer loops.

Standard 'time' triggers look like

{type='time', tag=<tag>, time=<str>}

tag=<tag> is the (optional) word provided last in the time rule and helps us to identify what time rule triggered our scene.

time=<str> is the time the rule is invoked (in HH:MM format)

 

Here we also add the keyword 'log' under "%% time" to instruct the time scene to send us log statements.

Log statements come in two versions

{type='time', tag='log', log=<msg>}

Standard log messages are sent at startup and at midnight with the log msg containing information about the time rules scheduled. It's usually nice to get some feedback that the Timer scene has scheduled our scene and for what times.

{type='time', tag='error', log=<msg>}

Error messages are always sent (even without the 'log' keyword). Usually due to time rules being faulty.

 

When you enable log messages your scene will also be triggered by log messages and not only timers. You need to tell them apart. A good way to test for a time trigger that is not a log message is to test if there is a .time field in the source trigger

if sourceTrigger.type=='time' and sourceTrigger.time then
   --- do whatever
end

IN that case we know that it is a proper time trigger and not a log message, as they lack the .time field.

 

Ok, the time rules in the example are

15:00 bar

This means that the scene is called 15:00 every day, with the identifier tag "bar"

In the scene we get an sourceTrigger at 15:00 of type

{type='time', time='15:00', tag='bar'}

In the above example we just print out the tag.

*00:15 test

creates a repeating timer that triggers the scene every 15 minutes, and with the tag "test". We get a sourceTrigger of type

{type='time', time='*00:15', tag='test'}

 

The generic version of a time description looks like

%% time
<time list> <conditions> <tag>
  :
<time list> <conditions> <tag>

Examples

  • 15:00,17:00 test
    creates two timers at 15 and 17 with the same tag "test".
  • sunrise test
    'sunrise' is a valid time descriptor and evaluates to todays sunrise hour.  'sunset', "dawn", and "dusk" are available too.
  • sunrise-00:10 test
    We can do simple arithmetic on times (+/-) to create offsets.
  • 15:00 wednesday test
    timers at 15 but only on wednesday, tag "test".
  • 15:00 wed test
    days can be shortened
  • 15:00 wed,fri test
    or listed.
  • 15:00 wed..fri test
    or intervals.
  • 15:00 1..7 test
    interval with first to seventh day of the month
  • 15:00 lastday test
    Only on the last day of the month
  • 15:00 lastweek test
    only in the last week of the month (number_of_days_in_month-6..number_of_days_in_month)
  • 15:00 monday lastweek test
    conditions can be combined. True for Monday of the last week
  • 15:00 monday lastweek may..aug test
    month conditions also available.
  • *00:15 test
    interval, every 15min starting immediately (when the script starts). Ex. 12:08:33, 12:23:33, 12:38:33 ...
  • +00:15 test
    interval, every 15min, starting on next even 15min interval. Ex. 12:15:00, 12:30:00, 12:45:00 ...
  • +00:15 weekends 10:00..15:00 test
    combined with weekend test (sat..sun) and time interval (10:00 to 15:00)
  • +01:00 sunrise..sunset may..sep water
    every hour between sunrise and sunset and between May and September call scene with tag 'water'
  • 20:30 2020/05/28,2021/05/27,2022/05/26 earth_hour
    "long date" format
  • +01:00 oct/28/10:00..dec/30/07:00 tag
    Long date intervals. Year can be excluded and month can be the name of the month, and time can optionally be added at the end (not sunrise/sunset)

 

The <time list> should be seen as the times we want to schedule and the <conditions> as filters, excluding some times from the <time list>

The other way to look at the list of <conditions> is that the spaces are ANDs and commas are ORs. Ex."10:00 thu,sat lasweek tag" is "10:00 ((thu OR sat) AND lastweek), tag"

 

The complete set of conditions are:

  • <time>..<time>, time interval in HH:MM or HH:MM:SS (but also 'sunset' and 'sunrise')
  • <week day>..<week day>, day interval. mon..wed,  Thursday..Saturday
  • <day number>..<day number> - 1..7, first to second day in current month
  • <month>..<month>, month interval
  • <time>,<time> -- One or more time specifiers. Ex. 10:00,11:00
  • <week day>,<week day> -- One or more day specifiers. Ex. monday
  • <day number>,<day number> -- Ex. 1,8,15,22
  • <month>,<month> -- One or more month specifiers. Ex. june,july,august
  • <long date>..<long date> - YYYY/MM/DD/HH:MM. Year cane be left out and time part is optional
  • <long date>,<long date> - 2020/may/11/10:00, may/11/10:00, may/11
  • alldays - same as mon..sun
  • weekends - same as sat..sun
  • weekdays - same as mon..sun
  • lastday - true if last day of the month
  • lastweek - true if last week in the month
  • true - always return true. See example below using fibaro global to disable rules
  • false - always return false. Effectively disabling the rule.

...and they can be combined.

 

It's allowed to end a time rule with a comment '--'. It's just removed before parsing the rule. (Wouldn't it be nice to be able to add comments to all headers?)

Ex.

15:00 monday test -- Trigger scene every Monday at 3 PM

 

A simple example turning on a lamp (with deviceID 55) at sunset-10min and turning off the lamp at sunrise+10min on weekdays

--[[
%% time
log
sunset-00:10 weekdays turnOn
sunrise+00:10 weekdays turnOff
--]] 

print("Scene started")

-- Redefine fibaro:getSourceTrigger
do local a,b=fibaro;b=a.getSourceTrigger;function a:getSourceTrigger()local c=b(a)local d=a:args()if type(d)=='table'and d[1]and type(d[1])=='table'then if d[1].type~=nil then return d[1]end end;return c end end

local st=fibaro:getSourceTrigger()

if st.type=='time' and st.tag='turnOn' then
  fibaro:call(55,"turnOn")
end

if st.type=='time' and st.tag='turnOff' then
  fibaro:call(55,"turnOff")
end

--[[
-- Another solution
if st.type=='time' and st.time then fibaro:call(55,st.tag) end
--]]

 

The tag is useful to identify what timer you get so you don't have to test against time again (or you can use it as in the example above, as an argument to a function, fibaro:call in the above case).

We can also let the tag be the name of a function called in our scene at that time. .

--[[
%% properties
%% events
%% globals
%% time
log
15:00 bar
*00:15 test
%% autostart
--]] 

print("Scene started")

-- Redefine fibaro:getSourceTrigger
do local a,b=fibaro;b=a.getSourceTrigger;function a:getSourceTrigger()local c=b(a)local d=a:args()if type(d)=='table'and d[1]and type(d[1])=='table'then if d[1].type~=nil then return d[1]end end;return c end end

local st=fibaro:getSourceTrigger()

function bar()
  print("Bar called")
end

function test()
  print("Test called")
end

if st.type=='time' and st.time then
  fibaro:debug(string.format("Triggered:%s, tag:'%s'",st.time,st.tag))
  if _ENV[st.tag] then _ENV[st.tag]() end
end

 

A more advanced feature is that time rules allows for substituting in values from fibaro globals.

Ex.

<myTime> monday test

This will fetch the value from the the fibaro global "myTime" and insert whatever value it has instead of <myTest>. If the value was "10:00" the rule would be

10:00 monday test

The substitution can be anywhere in the rule and contain anything so be careful.

We also watch if the value of "myTime" changes and if it does it will update the timers for the scene.

One way to use this is to have a global, ex "Stop" that is set to "true" or "false". If we include that in a rule

10:00 monday <Stop> test

We can easily enable/disable the rule depending on what we set "Stop" to.


What's New in Version 2.3   See changelog

Released

Improved time format

  • Like 6
  • Thanks 3

Other Files from jgab


User Feedback

You may only provide a review once you have downloaded the file.


Sankotronic

   2 of 2 members found this review helpful 2 / 2 members

Thank you @jgab for sharing your admirable knowledge! Respect!

 

Link to review
cag014

   1 of 1 member found this review helpful 1 / 1 member

Well done. I take off my hat to you

Link to review
Matz1977

  

Awesome feature... exactly what I was looking for to restart my HC2 on a regular basis!

 

One question regarding the substitution of values from fibaro globals, if I may: how would you store a time like 10:00 in a global variable? Fibaro does not accept the : character!?

 

My basic issue is that I need to use the trigger times and days I configured in the %% time header later in my scene code. Say, I want to send a push notification warning 5 minutes before scheduled reboot time (as configured in the header) and in addition, I want this warning lead-time of 5 minutes to be easily configurable (also in the scene header or by making use of global variables). So either use global variables in the %% time header and in my code or somehow fetch the configured times and days from the %% time header into local variables for my code.

 

How would you do that?

 

Kind regards,

Matz

Response from the author:

The variables panel is not so good to add general text.

If you choose the predefined variables (at the bottom) you can setup a couple of text strings like "00:05", "00:15" etc.

Otherwise from Lua you can set variables to any text string fibaro:setGlobal("var","00:10")

Link to review
gfoden@bit.org.uk

  

This functionality is brilliant. Not only does the code work but the best bit is the simplicity and elegance of the interface.

 

The timer handling is not well developed for LUA code: its all there but you need to really know LUA well to use it simply. I'm sure many people who are moderate programmers in other languages only know a smattering of LUA (enough to get by). The existing timer support requires you to edit the resulting timer vector and then do character comparison including punctuation. The scene has to be run repeatedly and the comparison repeated until it matches. I'm sure most people use the graphical blocks scene method to detect a time event then switch to LUA and copy the code. 

 

This interface is trivially simple and sits in the scene where it belongs.  (This is a big improvement on having to put the schedule into some other scheduler scene.) As well as being simple it is also well developed with plenty of extra functionality if you need it. All the execution logic is somewhere else and you do not have to worry about it. It allows the user to think of the user scene as something that is realistically initiated by events - in this case a time. It further means that the scene can be executed once from the event to the end, the concurrency limit can be set to one and the scene logic becomes almost trivial.

 

I only hope that Fibaro see fit to make this code part of the HC2 offering. That way the source trigger logic can be trivially integrated, which would add further simplification.

 

HC2 has moved from a pure object system to one with events. This addition is just a further practical step on the path to simplify the whole thing by adding "a time" as an event, which of course in the real world we live in, it is.

Congratulations to Jgab. 

Link to review
AR27690

  

Very useful scene indeed, but on the first run, block scene stopped to be triggered. 

After reboot works as expected...

Thank you for sharing your tremendous knowledge.

Response from the author:

 

That is a strange coincidence. The time scene doesn’t touch anything except lua scenes with a ‘%% time’ header. Could it have been an unrelated coincidence?

Link to review
×
×
  • Create New...