petrkl12 66 Share Posted October 29, 2018 (edited) 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 October 29, 2018 by petrkl12 Quote Link to post Share on other sites
jgab 898 Author Share Posted October 30, 2018 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? Quote Link to post Share on other sites
jompa68 121 Share Posted November 1, 2018 @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'))") Quote Link to post Share on other sites
jgab 898 Author Share Posted November 1, 2018 (edited) 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 November 1, 2018 by jgab Fixed log of Fibaro:* calls to include date Quote Link to post Share on other sites
jompa68 121 Share Posted November 2, 2018 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'))]]) Quote Link to post Share on other sites
petrkl12 66 Share Posted November 3, 2018 @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? Quote Link to post Share on other sites
jgab 898 Author Share Posted November 3, 2018 OR is a single vertical bar '|'. Rule.eval("@20:00 & (wday('sat,sun') | $BankHoliday>0 | $HolidayTime>0) => 125:on") Quote Link to post Share on other sites
petrkl12 66 Share Posted November 3, 2018 (edited) @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 November 3, 2018 by petrkl12 Quote Link to post Share on other sites
jgab 898 Author Share Posted November 3, 2018 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. Quote Link to post Share on other sites
petrkl12 66 Share Posted November 3, 2018 OK, restart is working my rules are generating via VD (json table) so there is no possibility to use variables for solving my case Quote Link to post Share on other sites
jgab 898 Author Share Posted November 4, 2018 (edited) 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 November 9, 2018 by jgab Quote Link to post Share on other sites
petrkl12 66 Share Posted November 4, 2018 interesting what is possible to do with your framework btw: check your code - fibaro:startScene(schedulerID,{urlencode(json.encode(args))}) Quote Link to post Share on other sites
jgab 898 Author Share Posted November 4, 2018 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. Quote Link to post Share on other sites
petrkl12 66 Share Posted November 4, 2018 @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 Quote Link to post Share on other sites
jompa68 121 Share Posted November 5, 2018 @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()]]) Quote Link to post Share on other sites
jgab 898 Author Share Posted November 5, 2018 (edited) 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 November 8, 2018 by jgab Quote Link to post Share on other sites
petrkl12 66 Share Posted November 5, 2018 OK, I know but I need to use predefined table variables with IDs ... Quote Link to post Share on other sites
jgab 898 Author Share Posted November 5, 2018 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()]]) 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 Quote Link to post Share on other sites
jompa68 121 Share Posted November 5, 2018 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. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.