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
Question
jgab 1,921
Hi,
I have been tinkering with my own
Please login or register to see this link.
for some time now. I know that there are many good examples out there like GEA but I like to write my own code - easier to debug that way . The reason I post here is that I would like to share my experience and code for anyone that wants to play with it. Maybe it can inspire someone to create something more useful for the community...? I'm continuously being inspired and learning things from this community, thanks!Current version:
Please login or register to see this attachment.
Warning1: Using this code is on own risk, it is not easy and there are millions of ways to make mistakes. Just getting the json syntax correct after a glass of wine is a challenge
If you want an "automated home" instead of a "programmable home" I really recommend purpose built scenes from Sankotronic, Jompa etc... To use this some coding skills (way of thinking) is required - I've written the code and I still make mistakes defining rules
I couple of goals with this project;
Please login or register to see this link.
(Thanks toPlease login or register to see this link.
). From ZeroBrane, triggers do not work but can be simulated by queuing up events or from the console. There isa limited complete offline mode. See variables in the beginning of the scene (_remote and _offline)It is still under development (templates, better debugging, state-machine format? etc.) but the reason I post now is that it currently runs what I did in all my old scenes. I will continue to update it as it progress. Good ideas are welcome!
Disclamer2: I'm not sure I have tested all parts of my code as I mostly run my own rules, e.g. there are bugs for sure. It may lack some obvious commands (RGB) etc. because I don't have these devices. However, it is easy to add, see below.
The format for a rule is a json array on toplevel
Please login or register to see this code.
each <expr> is evaluated from left to right and stops if an <expr> return false. Typically the leftmost expressions tests if some conditions are true and the rightmost carry out the actions that should happen in that case.
A generic <expr> is a json value/pair of format:
Please login or register to see this code.
<expr1> is the implicit first parameter/argument to the command. Ex.
Please login or register to see this code.
Arguments can be json arrays for many commands. A json string that starts with '$ is interpreted as a variable. Variables are defined outside the rules with the lua function:
defvar(name,value,index)
Ex.
Please login or register to see this code.
the value can be arbitrary deep ex. 'house.floor.room.lamp'. A common root like with homeTable/JTable also works well.
the 'index' parameter makes a reverse map so given the value the variable can be found, and allows the debugging to show the variable name instead of just the value. '$phone.tom' is a 'macro' that expands to {'var':'phone.tom'}, an expression returning the variables value
Similar '@var' expands to {'glob':'var'}, which accesses a global variable e.g. fibaro:getGlobal('var')
Variables are auto created at first use in an json expr if not already defined with defvar.
Many commands are tests and return true/false
Ex.
Please login or register to see this code.
This tests if the lamp in room1 has been on for 3 hours , can also be expressed as
Please login or register to see this code.
The 'id command accepts a single device or an array of devices and a optional number of tests
The example also introduces '!macros, The are some json string on the format '!TEXT' that expands to expressions,
which is more convenient than writing the full json expression.
'!$test.x=$test.x+1' expands to
Please login or register to see this code.
'[email protected][email protected]+5' expands to
Please login or register to see this code.
'==' is used to test equality '!@timeofday==Night' expands to {'==':[{'glob':'timeofday'},'Night']}
'![10:00]' expands to {'time':36000}, that returns true if it is 36000/60 minutes since midnight, e.g if it is 10 o'clock.
'![07:50,11:33]' expands to a similar {'time':[...,...]} testing if time is between 7:50 and 11:33..
there is also a "bracketed" time test, '![05:00,$Sunrise,07:00]' that test for the middle time but not sooner than the leftmost and not later than the rightmost. Of course you can use expressions/variables etc that evaluates to seconds of the day. In fact !'!10:00' is just a macro expanding to seconds
Standard rules that are run on specific intervals are declared:
Please login or register to see this code.
Ex.
Please login or register to see this code.
This decalares a rule that runs with the default 60s interval. It first checks if the lamp is on and has been on for 600s, if not it stops. Else it checks if the movement sensor is off and has been off for 600s. If that is true it continues to run the commands that turn off the lamp and log a statement to the fibaro console.
A rule to be run every 5 seconds is declared; event(5,"[... ]"). Many rules that run on different intervals can co-exist.
There are also trigger rules.
Please login or register to see this code.
Ex.
Please login or register to see this code.
Assuming that 331 is the id of the toilet sensor. So the trigger syntax is "{'value':331}". Predefined $vars are allowed too, ex. "{'value':'$room1.lamp'}"
To invoke this rule, '331 value' must be declared in the scene header (as normal is done) for the scene to receive the event.
Implementation detail: The first instance of the scene runs the scheduler of standard rules and normally never terminates. Additional instances of the scene starts when it triggers on incoming events declared in the scene header. For now it supports sourcetriggers that are values, globals, CentralSceneEvents, and SceneEvents. It then uses a shared global (that is autocreated) to hand the event over to the first scene instance so that the declared trigger rule can be run in that context sharing local variables. It uses a simple read/write flag model to handle syncronization and I have stress tested it and have seen no race conditions. However, it is best to set a high number of allowed instances for the scene to be on the safe side. The method introduces a small delay (average 250ms) on events. Many events triggered at the same time may also cause additional small delays. However, to have these two programming models (polling and triggering) converge in one scene instance is worth it... I will implement more types of events in the future. Planning to buy a Fibaro keyfob to play with that new event format...
So the normal time testing commands test against minutes. Ex. event("['![10:00]',...]")
and because that example runs at the default interval every 60s, it works very well. However, if the rules run more often i.e. event(30,"['![10:00]',...]") the rule will trigger twice the first minute 10'oclock. To avoid this there is a 'once' command, {'once':'![10:00]'} (shortened in macros to '!^[10:00]') that only returns true if the value has changed since last time.
This is also useful for rules like event("['!^@Timeofday==Day',...]"). If not 'once' was used it would be true every minute when the rule was run and not just the first time 'Timeofday' was set to 'Day'. Now '@Timeofday==Day' need to be false before it will return true again. There is also a {'truefor':<expression>, 'val':<seconds>} that returns true only if the expression has been true for a certain time (I haven't found a great use for this myself yet. Maybe if you want to send a notify every x minute if a door is open...)
Anyway, beacuse rules are invoked at predefined intervals, some thinking is needed to get rules correct. Watch up with movement sensors reset times, if people turn on and off switches in shorther intervals than your rules are run etc. The 'last' property gives the number of seconds since the device changed value is very useful to get around this...
Oh, there is also the 'cron' like time/date test to make more flexible time conditions:
'!{<min> <hour> <day> <month> <weekday>}' and expands to {'interval':'<min> <hour> <day> <month> <weekday>'}.
It uses the standard UNIX cron format also used and explanied in my
Please login or register to see this link.
Ex. '!{0/15 9,13,17 * jun-aug sat-tue}' Is true every 15 minutes on the hour 9,13, and 17 during June to August at Saturday,Sunday,Mondat, and Tuesday... '!{0 0/2 * * sat,wed}' is true once every even hour on Saturdays and Wednesdays.Ok, there are many more features. There is a '!!' macro that turns on logging of a rule. That is convenient because logging can be turned on after some tests have returned true to not litter the logg to much. i.e. event("['![10:00]','!!',...]") only logg if it is 10... ('!!' macro expands to {'startdebug':true})
Logging can also be turned on per rule; event("['![10:00]',...]").debug(true)
Debugging can also be turned on for all rules by setting the local lua variable 'debugAllRules' to true. There is also a 'debugLevel' that controls output and a 'debugProps' that if true debugs every call to fibaro:getValue and fibaro:getGlobal.
Per default a statistics is shown in the log every night at 1:00, showing how many rules and triggers have been invoked.
$variables can be functions. defvar('test',function() return 'Hello' end) will make '$test' return 'Hello'. '$Sunset','$Sunrise','$Now' is predefned variables using that approach.
New primitive commands are quite easy to add to the "engine" with the defun function.
Ex. I have the Sonos VD to play sound and a playSonosSound(file,volume,duration) lua function. To add a {'playfile':<filename>, 'volume':<expr>} for that ;
Please login or register to see this code.
Functions can also be defined in json. Ex.
Please login or register to see this code.
If the scene is run remote in ZeroBrane using FibaroSceneAPI, live triggers does not work. However there is a hack. If you suspend the program and go to the Local Console window you can type lua expression for evaluation. Three commands are available to queue up events.
Please login or register to see this code.
qVal(331,'value,60,1) while queue a "331 value" event to be received in 60s and the value of 331 will be 1. See examples in function initOffline() in the src code.
If I figure out how to do non-blocking read from the ZeroBrane console I could provide a more convenient command line function....
One anomaly with the FibaroSceneAPI is that it calls os.exit() if something goes wrong, i.e trying to access a non-existing deviceID on the HC2 and I can't catch that error and the scene terminates.
The engine is pretty efficient, commands try to optimize themeselves when they are invoked the first time, and I'm trying to avoid stressing the GC too much by being restrictive on temporary datastructures created while running. I see very little CPU load with 40+ rules and 10+ triggers.
The commands available now are;
Please login or register to see this code.
Second release: , Supports CentralSceneEvent. '$_src' is set to fibaro:getSourceTrigger() which can be used in the trigger to decide keys etc.
Ex. Keyfob with id 362.
Please login or register to see this code.
To check a key sequence this works:
Please login or register to see this code.
Release 03/14: , Fixed subtle trigger bug
Release 03/14: , Now supports functions. A small step towards templates... $variables are now auto created at first use if not previous defined with defvar(...).
Release 03/16:
Please login or register to see this attachment.
, New functionality; init(<expr>), quoting of args, generic fibaro:call, etcRelease 03/18: ,
Please login or register to see this attachment.
,Bugfixes, better error handling, better offline support. "out-of-the-box" it runs the simulation offline.Release 04/03:
Please login or register to see this attachment.
, New syntax and better simulation supportMacros:
...to be documented - for now see text above.
Link to comment
Share on other sites
1 answer to this question
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.