Jump to content

Recommended Posts

Hello,

 

I have created those rules for dishwasher etc.:

rule("for(00:03,dishwasher:power>10) & dishwasherState==0 => dishwasherState=1; log('dishwasher - working')

rule("for(00:02,dishwasher:power<5) & dishwasherState==1 => dishwasherState=0; log('dishwasher - finished')

 

it works BUT it could be even better to calculate weighted average for power (based on time between incoming values) because disadvantage of current rule that only one very short power decrease under 10 W can restart trigger

 

Do you have any suggestions how to improve? Thanks

 

Edited by petrkl12
Link to post
Share on other sites
  • Replies 2.6k
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

Note. The first ~2000 posts of this thread is mainly about EventRunner3 that is for the HC2. EventRunner3 is not developed further, but bugs are fixed as they are reported. For HC3, the version i

I've been playing with the HC3 a bit  (I don't own a HC3 but a friend has allowed me to remotely login to do testing - I'm very grateful for that). ...and I have made some progress with EventRunn

Working with event runner rules it can be useful to understand a bit how they work behind the "hood". Rules are always on the form   <test> => <statments> Ex 88:breached

Posted Images

All machines behave differently but this looks similar to rules I have used. The idea with a flag (dishwasherState) to not re-trigger makes sense. I would not have a 'for' construct in the first rule, because as soon as it starts to draw significant power it's on, or?

Rule.eval("dishwasher:power >10 & !dishwasherStarted => dishwasherStarted=true; log('dishwasher - working')")
Rule.eval("for(00:02,dishwasher:power < 5) & dishwasherStarted => dishwasherStarted=false; log('dishwasher - finished')")

I would test the flag against nil, because that works the first time if it's an uninitialised variable (i.e nil), otherwise you need to set it to 0.

However, the issue is the second rule, how long (after it has been started) does it need to be lower than 5 to consider to be finished? 2 minutes may be enough.

If you have problem with this can you give an example of the machine's "power behaviour" that creates issues with restarting the trigger?

If I run this

  dishwasher=77
  
  Rule.eval("dishwasher:power >10 & !dishwasherStarted => dishwasherStarted=true; log('dishwasher - working')")
  Rule.eval("for(00:02,dishwasher:power < 5) & dishwasherStarted => dishwasherStarted=false; log('dishwasher - finished')")
  
  Rule.eval("wait(t/10:10); dishwasher:power=7")
  Rule.eval("wait(t/10:11); dishwasher:power=11")
  Rule.eval("wait(t/10:11:02); dishwasher:power=7")
  Rule.eval("wait(t/10:11:04); dishwasher:power=11")
  Rule.eval("wait(t/10:34); dishwasher:power=4")
  Rule.eval("wait(t/10:34:05); dishwasher:power=5")
  Rule.eval("wait(t/10:34:06); dishwasher:power=3")

I get this running offline

07:48:02:Tue Oct 30: Demo - EventRunner v1.2
07:48:02:Tue Oct 30: Loading rules
07:49:02:Tue Oct 30: Rule:1:dishwasher:power >10 & !dishwasherStarted => dishwasherStarted=true; log('dishwa
07:49:02:Tue Oct 30: Rule:2:for(00:02,dishwasher:power < 5) & dishwasherStarted => dishwasherStarted=false;
07:49:02:Tue Oct 30: Scene running
07:49:02:Tue Oct 30: Sunrise 06:00, Sunset 18:00
10:10:00:fibaro:call(77,"setPower",7)
10:11:00:fibaro:call(77,"setPower",11)
10:11:00:Tue Oct 30: dishwasher - working
10:11:02:fibaro:call(77,"setPower",7)
10:11:04:fibaro:call(77,"setPower",11)
10:34:00:fibaro:call(77,"setPower",4)
10:34:05:fibaro:call(77,"setPower",5)
10:34:06:fibaro:call(77,"setPower",3)
10:36:06:Tue Oct 30: dishwasher - finished
Max time (_speedtime), 192 hours, reached, exiting

Btw, running offline it seems like there was a problem with the fake "setPower" not triggering in the right way. Get the new version from Github of EventRunnerDebug.lua and it should be fixed - maybe that was your problem?

Link to post
Share on other sites

@jgab can you see what is wrong with this syntax? i dont get the push to phones

  Rule.eval("@17:00 & wday('sun') & wnum % 2 == 0 => iOS.MonasiPhoneX:msg='Vattna blommorna!'")
  Rule.eval("@08:09 & wday('mon') & wnum % 2 == 1 => iOS.iPhoneXJonny:msg='Vattna blommorna!'")
  Rule.eval("for(00:40,893:isOn) => 893:off;iOS.iPhoneXJonny:msg=log('Stänger av kaffebryggaren %s',osdate('%X'))") 

 

Link to post
Share on other sites

If I run this (simulated) 

 iOS={MonasiPhoneX=88,iPhoneXJonny=99}
  Rule.eval("@17:00 & wday('sun') & wnum % 2 == 0 => iOS.MonasiPhoneX:msg='Vattna blommorna!'")
  Rule.eval("@08:09 & wday('mon') & wnum % 2 == 1 => iOS.iPhoneXJonny:msg='Vattna blommorna!'")
  Rule.eval("for(00:40,893:isOn) => 893:off;iOS.iPhoneXJonny:msg=log('Stänger av kaffebryggaren %s',osdate('%X'))")
  
  Rule.eval("wait(+/01:00); 893:on")

I get this

15:20:19:Thu Nov 01: Demo - EventRunner v1.2
15:20:19:Thu Nov 01: Loading rules
15:20:19:Thu Nov 01: Rule:1:@17:00 & wday('sun') & wnum % 2 == 0 => iOS.MonasiPhoneX:msg='Vattna blommorna!'
15:20:19:Thu Nov 01: Rule:2:@08:09 & wday('mon') & wnum % 2 == 1 => iOS.iPhoneXJonny:msg='Vattna blommorna!'
15:20:19:Thu Nov 01: Rule:3:for(00:40,893:isOn) => 893:off;iOS.iPhoneXJonny:msg=log('Stänger av kaffebrygga
15:20:19:Thu Nov 01: Scene running
15:20:19:Thu Nov 01: Sunrise 06:00, Sunset 18:00
16:20:19:Thu Nov 01: fibaro:call(893,"turnOn")
17:00:19:Thu Nov 01: fibaro:call(893,"turnOff")
17:00:19:Thu Nov 01: Stänger av kaffebryggaren 17:00:19
17:00:19:Thu Nov 01: fibaro:call(99,"sendPush","Stänger av kaffebryggaren 17:00:19")
17:00:00:Sun Nov 04: fibaro:call(88,"sendPush","Vattna blommorna!")
08:09:00:Mon Nov 05: fibaro:call(99,"sendPush","Vattna blommorna!")
Max time (_speedtime), 192 hours, reached, exiting
Memory start-end:-16.72

...and the first "Vattna blommorna" is on a Sunday and the second on a Monday...

Does nothing at all happen for you?

PS. 893 is a very high deviceID...?

Edited by jgab
Fixed log of Fibaro:* calls to include date
Link to post
Share on other sites

I have adjusted the line now and now it works

  Rule.eval([[for(00:40,893:isOn) => 893:off; iOS.iPhoneXJonny:msg=log('Stänger av kaffebryggaren %s',osdate('%X'))]])

 

Link to post
Share on other sites

 

@jgabI want to run ie. lights during weekends, bankholidays and my holidays

-- this is not correct - I need OR
Rule.eval("@20:00 & wday('sat,sun') & $BankHoliday>0 & $HolidayTime>0 => 125:on")

-- something like
Rule.eval("@20:00 & (wday('sat,sun') OR $BankHoliday>0 OR $HolidayTime>0) => 125:on")

How to solve it?

Link to post
Share on other sites

OR is a single vertical bar '|'.

Rule.eval("@20:00 & (wday('sat,sun') | $BankHoliday>0 | $HolidayTime>0) => 125:on")

 

 

Link to post
Share on other sites

@jgab

 

I have some function that creates dynamicly rules and I have user event that I can call from other scenes or vd to ask fo reload dynamic rules

 

Is it possible to reload some rules in that function - it means delete old one and add new one in running EventRunner scene

 

Now I have solved it via stop and restart whole scene but maybe you have better sollution ...

 

-- simple example
function DynamicRules(num)
  for i=1,num do
    Rule.eval("@00:0"..tostring(i).." => Log('test:"..tostring(i).."')")
  end
end

function Main()
  define('DynamicRules',DynamicRules)
  DynamicRules(6)
  -- here I want to reload - delete old one and add new one
  Rule.eval("#EventDynamicRules{value='$idd'} => log('EventDynamicRules=%s',$idd); DynamicRules($idd)")
 end

 

 

Edited by petrkl12
Link to post
Share on other sites
1 hour ago, petrkl12 said:

@jgab

 

I have some function that creates dynamicly rules and I have user event that I can call from other scenes or vd to ask fo reload dynamic rules

 

Is it possible to reload some rules in that function - it means delete old one and add new one in running EventRunner scene

 

Now I have solved it via stop and restart whole scene but maybe you have better sollution ...

 

-- simple example
function DynamicRules(num)
  for i=1,num do
    Rule.eval("@00:0"..tostring(i).." => Log('test:"..tostring(i).."')")
  end
end

function Main()
  define('DynamicRules',DynamicRules)
  DynamicRules(6)
  -- here I want to reload - delete old one and add new one
  Rule.eval("#EventDynamicRules{value='$idd'} => log('EventDynamicRules=%s',$idd); DynamicRules($idd)")
 end

Well, not in general. I recommend to restart the scene. I'm working on a way to enable/disable rules and I guess add/remove could work too. The trick is that some rules create timers and there are a lot of things to consider when disable/removing a rule.

In some cases the "dynamic" behaviour can be solved by using variables. In trigger rules, the left hand side can not change variables bound to deviceIDs or globals as these are defined when compiled (Rule.eval). However, dailys as you have in your example is scheduled every midnight, so these can can change (that's why an expression is allowed after '@', like @15:00+rnd(-01:00,01:00)...

However you probably need more dynamic reloading of rules and I would recommend restarting the scene for now.

I have some scenes that watch if the HomeTable changes. If it changes it sends an event to a supervisor scene asking to be restarted and then kills itself with fibaro:abort(). The supervisor scene waits a few seconds and the restarts the scene. I guess a similar schema could be used for reloading new rules.

 

Link to post
Share on other sites

OK, restart is working 

my rules are generating via VD (json table) so there is no possibility to use variables for solving my case

 

Link to post
Share on other sites

Here is the most simple and complex scheduler. 

Here we have an EvenRunner based scene called "CronRunner" that is set to autorun and should never need to be tinkered with. Think about it as a "service" that is just deployed on the HC2 and run by itself.

However, it's a great helper when writing other scenes to get callbacks at specific times (If you are a unix person, it's like registering jobs with crontab).

So, we can write a simple scene (a basic scene, not based on the EventFramework) like this

--[[
%% properties
%% events
%% globals
--]]

local trigger=fibaro:getSourceTrigger()

if trigger.type=='other' and fibaro:args() then
  trigger=(fibaro:args()[1]):gsub('%%(%x%x)',function (x) return string.char(tonumber(x,16)) end)
  --fibaro:debug("Callback:"..trigger)
  trigger =  json.decode(trigger)
elseif trigger.type=="autostart" or trigger.type=="other" then
  local cronID,first = 7,true
  fibaro:debug("Waiting to startup")
  fibaro:sleep(1000*(3+math.random(5)))
  function add(args)
    args.type,args._from,args._first = 'add',__fibaroSceneId,first
    fibaro:startScene(cronID,{urlencode(json.encode(args))})
    fibaro:sleep(100)
    first=false
  end
  
  fibaro:debug("Registering callbacks")
  add{scene=cronID,time="sunrise +15 * * *",callback="Morning"}
  add{scene=cronID,time="sunset -15 * * *",callback="Evening"}
  add{scene=cronID,time="0 8-17 * * mon-fri",callback="HourlyWorkingWeekdays"}
  add{scene=cronID,time="0 20 lastw-last * mon",callback="20:00LastMondayInMonth"}
  add{scene=cronID,time="0/15 17-23 * * *",callback="Every15minEvenings"}
end
  
if trigger.type=='callback' then
  
  if trigger.callback=='Morning' then
    fibaro:debug("It's morning!...")
    
  elseif trigger.callback=='Evening' then
    fibaro:debug("It's evening...")
    
  elseif trigger.callback=='HourlyWorkingWeekdays' then
    fibaro:debug("Checking the house...")
    
  elseif trigger.callback=='20:00LastMondayInMonth' then
    fibaro:debug("Put out the garbage...")
    
  elseif trigger.callback=='Every15minEvenings' then
    fibaro:debug("Start the fan...")
  end
  
 end 

When starting up (source trigger of 'autostart' or 'other' without arguments) the scene registers five callbacks at specified time intervals with the CronRunner services (ID 7) and terminates like scenes normally do. However, the CronRunner service will then do a fibaro:startService(scene, callback), i.e. starting the scene and giving it back the callback value at the specified time intervals.

 

In this case we have a simple if-then-else flow at the second half of the scene to determine what call-back we got and then carry out whatever task we need to do then. CronRunner is a very diligent service and will make sure you get the call-backs on the minute you specified without time drift.

There are some conditions needed to make this work. CronRunner have to start before the client scenes start. Otherwise there is no one answering when the client wants to register call-backs. Here we have introduced a random startup delay in the client scene of 3-8seconds. We introduce randomness in case we have many clients. If we stop and start the client scene, CronRunner is smart enough to remove the old call-backs and accept the new ones.

 

So lastly, how do we specify the call-back intervals? That's why the service is called CronRunner.

The format is inspired by the UNIX crontab service and format.

It's a flexible, tried and true, syntax for expressing time intervals for job scheduling. A bit tricky at first glance but every sysadmin knows how to setup tasks to run at specific times and intervals using that format. Crontab runs every minute and consists of time patterns and a tasks to be carried out when a time pattern is true. 
The pattern format has five fields:

"<minute> <hour> <day> <month> <weekday>"

 

<minute> -> 0 .. 59
<hour> -> 0 .. 23
<day> -> 1 .. 31
<month> -> 1 .. 12, or jan...dec
<weekday> -> 1 .. 7 , or mon..sun.  1 is Sunday and 7 is Saturday
 
 *         -> matches any value for that field. Ex. ‘*’ in minute field matches 0 .. 59
 x,y,z   -> matches the listed values for that field. Ex. ‘1,20’ in day field matches the 1st and the 20th day of month
 x-y     -> matches values for an interval for that field. Ex. ‘sun-sat’ in weekday field matches 1,2,3,4,5,6,7
 x/y     -> matches  values starting at x with y increments. Ex. ‘0/1’ matches ‘0,1,2,3,4,5,…    ‘0/15’ matches 0,15,30,45   ‘1/2’ matches ‘1,3,5,7…’
 x-y/z  -> combination of interval and increment.. Ex. ‘10-18/2’ matches ‘10,12,14,16,18'

last    -> in the day field stands for the last day number in the current month
lastw  -> in the day field stands for the day number of the first day in the last week of the current month

 

An exception to the format is when the minute field is replaced with 'sunrise' or  'sunset', then the hour field becomes offset in minutes


Some examples

"* * * * *"     - Every minute every hour every day every month

"0 * * * *"     - Every first minute of every hour every day every month

"0 0 * * *"     - Every first minute of every first hour (i.e. 00:00) every day every month

"10,30 * * *" - 10min and 30min past of every hour every day every month

"0 8-17 * * mon-fri"     - Every first minute in hours 8 to 17 on Monday to Friday 

"sunset -10 * dec-feb sat,sun"  - At sunset minus 10minutes on Saturday and Sunday in winter months

"sunrise +10 last * *"  - At sunrise plus10minutes last day of the month

"0 15 lastw-last * fri"    - at 15:00 the last monday in every month

"0 8-17/3 1/2 * *"    - every third hour between 8 and 17 on every odd daynumber  every month

 

Even though it's a flexible format, some intervals may need to be broken up in several intervals. Like 15min past every odd hour and 30min past every even hour - that's 2 patterns... At the moment it's not possible to specify in one pattern an interval from sunrise to sunset or vice versa, something that may come. 

Edited by jgab
Link to post
Share on other sites

interesting what is possible to do with your framework :)

 

btw: check your code - 

    fibaro:startScene(schedulerID,{urlencode(json.encode(args))})
Link to post
Share on other sites
4 minutes ago, petrkl12 said:

interesting what is possible to do with your framework :)

 

btw: check your code - 

    fibaro:startScene(schedulerID,{urlencode(json.encode(args))})

Thanks, got the idea to change the name when I posted - fixed it.

If one could add and remove eventscript rules, this principle could be used for that too... and make clients simpler with less code.

Link to post
Share on other sites

@jgabCould you please add support for following syntax:

 

define('Switches', {101, 102, 103})
rule("#CentralSceneEvent{data={deviceId=Switches}} => d=env.event.deviceID; log('Switch id=%s',d)")

 

now it's with error in function _match

 

Thanks

 

Link to post
Share on other sites

@jgab

testing this now and get error, need help.

-- iOSLocatator
  Rule.eval("#autostart => 456:start=#getLocations")
  Rule.eval("#location{user='$user',place='$place'} => log('User:%s place:%s',user, place)")
  locations = {}
  numberOfPeople = 2
  homeFlag = false

  Rule.eval("#presence{state='home',who='$who'} => phonesToNotify:msg=log('%s at home',who)")
  Rule.eval("#presence{state='allaway'} => phonesToNotify:msg=log('House empty')")

  function checkLocation()
    local home = false
    local who = {}
    for w,p in ipairs(locations) do
      if p == 'Home' then home=true; who[#who+1]=w end
    end
    if home ~= homeFlag then 
      homeFlag = true
      Event.post({type='presence', state='home', who=table.concat(who,',')})
    elseif #locations == numberOfPeople then
      if homeFlag ~= false then
        homeFlag = false
        Event.post({type='presence', state='allaway'})
      end
    end
  end

  Rule.eval([[#location{user='$user', place='$place'} => 
      locations[user]=place;
      phoneToNotify:msg=log('%s at %s',user,place);
      checkLocations()]])

image.png.9ef57014a233c4a831c3884ac57d7c84.png

Link to post
Share on other sites
On 11/4/2018 at 8:05 PM, petrkl12 said:

@jgabCould you please add support for following syntax:

 

define('Switches', {101, 102, 103})
rule("#CentralSceneEvent{data={deviceId=Switches}} => d=env.event.deviceID; log('Switch id=%s',d)")

 

now it's with error in function _match

 

Thanks

 

In the future I will support something like "Rule("Switches:csEvent => ..."), however that doesn't work now.

You can do "rule("csEvent(101) | csEvent(102) | csEvent(103) => ...") but you may want the a predefined table variables with IDs?

 

Edit. Ok, now Switches:central is supported

Edited by jgab
Link to post
Share on other sites
1 hour ago, jompa68 said:

@jgab

testing this now and get error, need help.

-- iOSLocatator
  Rule.eval("#autostart => 456:start=#getLocations")
  Rule.eval("#location{user='$user',place='$place'} => log('User:%s place:%s',user, place)")
  locations = {}
  numberOfPeople = 2
  homeFlag = false

  Rule.eval("#presence{state='home',who='$who'} => phonesToNotify:msg=log('%s at home',who)")
  Rule.eval("#presence{state='allaway'} => phonesToNotify:msg=log('House empty')")

  function checkLocation()
    local home = false
    local who = {}
    for w,p in ipairs(locations) do
      if p == 'Home' then home=true; who[#who+1]=w end
    end
    if home ~= homeFlag then 
      homeFlag = true
      Event.post({type='presence', state='home', who=table.concat(who,',')})
    elseif #locations == numberOfPeople then
      if homeFlag ~= false then
        homeFlag = false
        Event.post({type='presence', state='allaway'})
      end
    end
  end

  Rule.eval([[#location{user='$user', place='$place'} => 
      locations[user]=place;
      phoneToNotify:msg=log('%s at %s',user,place);
      checkLocations()]])

image.png.9ef57014a233c4a831c3884ac57d7c84.png

Oh, you found a bug that I introduced some time ago. table[<expression>] compiled wrong. <expression> in your case was the variable 'user'. I have fixed it and pushed a new version. Thanks /J

Link to post
Share on other sites

 

56 minutes ago, jgab said:

Oh, you found a bug that I introduced some time ago. table[<expression>] compiled wrong. <expression> in your case was the variable 'user'. I have fixed it and pushed a new version. Thanks /J

Working without errors now, thanks.

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...