I’m stuck here and I do not find much here in the forums or through Google. Or maybe I just do not understand how to read and use what I found.
What I try to achive is creating global functions (like I know from PHP) which I can use in my rules. Especially notifications are the same for most of my rules, like for example:
sendXMPP("roi@jabber", new DateTimeType() + ": Text")
sendTelegram("telegram1", new DateTimeType() + ": Text")
So I write the same again and again, also with the users and passwords and everything else (e.g. before sending a notification to Kodi/XBMC I check if the host is online).
So is there something I can use, like
MyOwnXbmcNotification("Topic","Text")
which I define somewhere as a globally usable function?
Thank you very much for pushing me in the right direction.
@KjetilA Thank you for your answer - although it does not sound very promising. Are global functions a feature which is planned for the future, or maybe already has a milestone? I am sure many people would like to see this as, at least for me, my problem does not look very special.
If I cannot use a global function, can I use something which I create at the top of the rules file maybe? Like a semi-global function which I can use for all rules inside one rules file?
The solution you suggested will work, yes, but does not sound very practical to me. Then I would stick to my over and over same definitions for the notifications and if I need to change something it will remain a cut-n-paste orgy.
I am not up to speed on this, but I believe this may be supported (in the future) by the “next-generation rules engine”, so let’s keep our fingers crossed,
Yes, this would be a “lambda function”. Take a look at this tutorial:
You may use scripts as some kind of global functions. But have to find a convention for yourself to use Items as parameters because you can not delegate script parameters to callScript(“filenameOfMyScript”)
I migth be missing the point, but what is the difference between the desired global functions and the lambdas shown above? Have all your rules and lambdas in a single file and you are set!
This is one of the driving requirements behind the creation of the Experimental Rules Engine. Kai’s vision is we would have ready made off the shelf rules that users can download and install from the IoT Marketplace. No more copy and paste from examples in the forum.
So, in addition to lambdas and what might be coming in the future, a great way to handle cross-cutting code like this is Design Pattern: Separation of Behaviors. This is particularly handy for notifications as it lets you add additional logic around alerts more easily (e.g. use a different mechanism at night than during the day).
@opus The big difference here is for me that putting all rules into one file creates a big pile of sh*t and looks like programming back in 1990 and also would not show the intention of the openHAB idea behind this - in my eyes. Also I imagine that putting everything into one file creates more problems, e.g. when putting a typo into one of these rules which causes all rules to fail at once as the updated file might not be loaded into openHAB anymore.
I think what opus was intending is not that you put ALL rules into a single file. If you organize your files by function, you will be very unlikely to need to access any lambdas from some other file. And if you do have a cross-cutting concern like alerting or the like, you can use the Separation of Behaviors to call those.
Well, the problem is that I e.g. want to make notifications from many different functions - so there we have the problem. I understood opus not like you. In my understanding he suggests putting everything into one big file and use lambdas then.
As usual, my answer was not as precise as the one from @rlkoshak.
The quick anser was to put everything into one file, the more elaborate(as said by @rlkoshak) is to seperate by function, by that the rules needing a specific lambda could be in a seperated file.
Since you do forsee the need for such a lambda in (nearly) all your rules you could find yourself in the single file situation.
@rlkoshak I completely agree with your thoughts about ‘programming in the 1990s’.
I have some complex rules, resulting if .rules files close to 64kB, thus it’s a bit of a nightmare to debug. It also takes very long after saving the file before it’s compiled and it starts executing.
So I tried to put a bunch of the lambda functions in a .script file.
However, you cannot call includeScript(“name_wo_ext”) outside a rule (thus global within the .rules file.
So my next try: include the .script in the rule. Yes, no error. However, I cannot call the lambda functions. It seems they only exist in the context of the .script file. Nasty!
Apparently there are no ways to trick the system.
At least, I couldn’t find them.
But you never know…
What I haven’t tried (since so far it always resulted in errors within the .rules files), is to define a class. I mean: is that somehow possible? In that case we might be able to simply use an include a.b.c at the top of the .rules file and call the methods.
Unfortunately I haven’t found any examples, so whether this is or isn’t possible… who knows.
In the meantime I’m a bit fed up with the limitations of xtend, so I’m doing more and more in PHP. For me this could end up in using openHAB as just the bus and the drivers for the hardware and doing all the automation logic in PHP.
A global lambda is a global variable. As a global variable is must follow all the same restrictions and scope of any other global variable. This means:
only can be accessed from the file they are defined in
cannot see any other global variables
are not thread safe
Since you cannot pass arguments to Rules DSL Scripts nor get a return value, lambdas are pretty much useless in that context.
There are not, and frankly if you are hoping to use lambdas to simplify your Rules you are probably heading down the wrong path anyway. See Design Pattern: DRY, How Not to Repeat Yourself in Rules DSL for better approaches. Or consider moving to one of the JSR223 supported languages.
No, not possible
Why not use JSR223 and code your rules in Jython, JavaScript, or Groovy instead and keep everything local?
Or if you want something easier to use configure NodeRed as your Rule engine.
Or, as illustrated in the DP I linked to above, there are techniques and best practices one can use to avoid duplicated code in Rules DSL that don’t involve lambdas. Based on experience, you can probably reduce your lines of code by 60-80% using those techniques.
Hi @rlkoshak,
Thank for the feedback.
I wasn’t aware that classes aren’t possible; that saves me some time in fruitless attempts.
Good point.
I haven’t explored the JSR223 route yet.
On the other hand I have my UI in apache/php anyway (it’s an alternative remote control UI, as well as a configuration UI for my automation rules – program once, configure many), so it’s not such a big step to do even more in php.
The reason why my rules are rather large, is because the OH rules are basically interpreting my configuration. Thus I don’t mind to use the OH REST API even more than I do now.
There’s another reason why I like php: it’s very well documented.
In my experience Xtend is a real struggle; never happened for such an elongated period for any other languages / libraries I’ve used over many years.
Anyway, I’m keeping an open mind – and I don’t mind completely redesign at a point in the future if some new promising route becomes available.
I know this is an old thread, but I stumbled upon this a few days ago via google. Since there isn’t an easy way to do global variables / functions in the default rule engine, I decided to code myself a tool to allow this.
I know the general advice to get reusable code is to use “virtual” items with a rule attached to them, but that gets messy quick in my opinion.
Anyway, if someone else also ends up here after googling like I did, here’s a solution for you, neatly packaged in a docker container: finalfrag/dynamicrules - Docker Image
It is the nature of libraries that they must be included. It would be chaos to include all libraries all the time. You must include them, and generally you should only include (or require in the case of JS) the specific functions that are in use in that file.
To help with writing managed rules (i.e. rules in the UI) the JS Scripting add-on deviates from best practice and will automatically require the helper library for you. But it makes it clear in the settings that you should not do so for file based rules. You could hijack this feature though to have your functions automatically included too. But you would have t:
install the helper library using npm (there is an option to do so through openhabian-config)
add your functions to the helper library
set the JS Scripting “JavaScript Environment” variable to “Inject everywhere”
set the JS Scripting “System Behavior” to disable caching of the helper library
However, this means you are now responsible to keep the helper library up to date. You are no longer using the library that comes with the add-on.
Also, every time you update the helper library, you will have re-add your functions to it because all your changes will be replaced.
But the original subject of this seven-year-old post concerns Rules DSL. And Rules DSL still does not support libraries and likely never will.
Loading of third party libraries is supported the same way as loading the openHAB JavaScript library:
var myLibrary = require('my-library');
Note: Only CommonJS require is supported, ES module loading using import is not supported.
Run the npm command from the automation/js folder to install third party libraries, e.g. from npm (opens new window). This will create a node_modules folder (if it doesn’t already exist) and install the library and it’s dependencies there.
There are already some openHAB specific libraries available on npm (opens new window), you may also search the forum for details.
You can also create your own personal JavaScript library for openHAB, but you cannot just create a folder in node_modules and put your library code in it! When it is run, npm will remove everything from node_modules that has not been properly installed.
Follow these steps to create your own library (it’s called a CommonJS module):
Create a separate folder for your library outside of automation/js, you may also initialize a Git repository.
Run npm init from your newly created folder; at least provide responses for the name, version and main (e.g. index.js) fields.
Create the main file of your library (index.js) and add some exports:
var someProperty = 'Hello world!';
function someFunction () {
console.log('Hello from your personal library!');
}
module.exports = {
someProperty,
someFunction
};
Tar it up by running npm pack from your library’s folder.
Install it by running npm install <path-to-library-folder>/<name>-<version>.tgz from the automation/js folder.
After you’ve installed it with npm, you can continue development of the library inside node_modules.
As you might have already noticed, the JavaScript Scripting add-on is reloading a script as soon as one of its dependencies changes. When developing a library inside node_modules, this can cause regular reloads. To avoid this situation, you can disable dependency tracking in the JavaScript Scripting add-on settings (you need to tick “show advanced” for the setting to come up).
It is also possible to upload your library to npm (opens new window)to share it with other users.
That’s because you didn’t create a module/library. OH is trying to load that file and process it as a rule.
Also, unless you are using Nashorn JS (in which case nothing mentioned so far is relevant at all) you are using the wrong folder. Your rules go in $OH_CONF/automation/js and your library will eventually be in $OH_CONF/automation/js/node_modules.
Seven years ago there was no JS Scripting add-on. So obviously nothing in the thread before your post applies to JS Scripting.
JS Scripting provides a node.js type environment. All these requirements for libraries come from node.js, not OH.
It is always better to open a new thread than resurrecting a years old thread, particularly when that thread doesn’t actually address your actual topic.