Making OH Rules Easier for Everyone

A statement like this does not belong in a thread titled as such. I do admire your humor and honesty though.

Hey, by the way, what JavaScript IDE you use? Is there any other way to create a bidirectional bridge from JavaScript to Java?
I used Rhino JS engine with JSR223 like integration 8 years ago, at that time it was the only option, we did build a nice product on top of it.
I am not sure about latest JS/Java integration for the back-end purposes.
I do agree with you that JS has the most extensive collection of UI centric libs and the node-JS backend libs.
I am trying to figure out how we leverage that.

What i’m trying to say is, i rarely work on code i don’t need personally and i’m the kind of dev who stops coding if something works “good enough”, i (mostly) don’t go out of my way to make it fool proof, i don’t really have the patience to explain things like that to non-techies either, i don’t like writing docs. I work as project manager / lead dev though so i guess i have some valuable input

I’m just using VS Code, Visual Studio, PHPStorm at work, sometimes. I’m not doing any Java stuff at all though, i have been successfully avoiding Java since school. :nerd_face:

1 Like

Yes, your input is valuable and appreciated.
Thanks

That’s not true. I’d love to see something better for new users come along. But I have definite opinions on what would be as easy to use or easier to use for non-technical users.

I’m of the opinion that there really can’t be a programming environment that has the training wheels necessary to help these new users and have the flexibility an experienced programmer desires. But I’m willing to be pleasantly surprised. That’s the main reason I’m even pasting here, besides the fact that the not-technical users need an advocate moreso than developers do.

These training wheels included

  • fewer options, not more
  • fewer ways to do something, not more
  • self descriptive syntax
  • clear delineation of scope and what goes with what
  • intuitiveness and consistency

Based on those criteria Rules DSL and Experimental Rules are better than what is proposed so far (I’ve not yet reviewed your most recent changes).

Perhaps it’s a fools errand to try to target both audiences with the same language. I’m perfectly willing to accept that. But that also means that Kotlin can’t be and won’t be a replacement for the Rules DSL which is what you are proposing, because it is worse than Rules DSL on these criteria.

You might have to choose between targets. And if you choose to focus on devs, that’s fine and appreciated and will probably be an improvement. But don’t advertise it as the language to use for new users and I really won’t have any useful opinions to offer so will happily bow out of the discussion.

Why? You are presenting this as a better replacement for Rules DSL. One area where Rules DSL is good is in its relative simplicity to learn for non-technical users. If you want to replace Rules DSL with this it needs to be at least as easy to learn and use for non-technical users. Otherwise it is just an addition for developers. That’s perfectly fine but it means it’s not a replacement for Rules DSL.

And if you are not trying to build an environment for non-technical users, I don’t have anything to add. You are a better Kotlin developer than I am. You know better what makes sense to be available to other Kotlin developers. I have nothing useful to add.

I guess my tl;Dr is if you want to target both types of users, I’ll continue to push back on anything that violates the criteria above. If you want to only target developers, I have no opinion. If you want to target only new users I’ll push back even harder.

I only speak for the non-technical users. I’ve said it before. It’s unfair to expect me to provide any opinions from any other perspective. I’ve already made clear what my role would be in this discussion. And it is unfair to expect me to be ok with compromises in favor of developers with no apparent immediate benefit to the non-technical users. My job is to advocate for the non-technical users and I’d be a poor advocate if I did.

Yep, you caught me in a typo.

No, because there were changes to rules dsl from oh 1 to oh 2 that broke the syntax checking and VSCode came along before anyone could fix ESH Designer. I’ve no idea if there is an IDE for Xtend but ESHD wasn’t it. ESHD was an IDE for OH. VSCode with the oh extension is an ide for OH. The vast majority of the syntax for rules is custom Rules DSL. The sorts of things both checked, and whether they provided the most benefit are:

  • syntax checking for Rules, Items, Things, and Sitemaps, not just Rules.
  • Item aware meaning if you refer to an Item that doesn’t exist they tell you.

A generic Kotlin idea isn’t going to do that.

As was Designer.

That is something I can get fully behind.

At the end of the day, if you want this to replace Rules DSL for non-technical users then my comments here are useful. If you want this to target other developers then my comments are not. I’ve already said me peace in that end (I’d like to see Python since it is known by far more developers and there are far far more scripts and libraries that imminent ha relevant APIs than in any other languages).

I just don’t think this is productive.

1 Like

Would not something like Flows Builder be better suited to meet the needs of non-technical users?

Assuming it became the new face of the Experimental Rules Engine (and was built-in and not requiring the added effort of setup and configuration like Node-Red), it seems to handle the basic needs of “when this happens, do that” using a clean visual presentation. Someone said it reminds them of Node-Red but, from appearances (I haven’t actually tried it), it appears to do more hand-holding than Node-Red. A quick look at this image shows lots of basic triggers/conditions/actions in the left-hand menu.

Apparently the developer has suspended developing it because of alleged instabilities in the Experimental Rules Engine and, I assume, is waiting for it to stabilize before continuing.

1 Like

Reminds me of the Girder 3/4 type of interface for building actions based off events with conditions. The treeview was a great way to build automation tasks easily.

Just a big thank you @rlkoshak for your extensive and as always extremely helpful post. Sorry for my silence, we are moving these days and aside from still waiting on our internet connection, I find myself distracted with hauling boxes and much more…which does not leave any room for other things.

Rich, and other members, please review the new syntax, the older one you reviewed was a little too much for newbies, I agree.
There is no reason why we cannot come up up with something as readable as Rules DSL and yet attract developers. I don’t believe in no win scenarios.
I have reduced the programming concepts needed to write a simple Kotlin rule to minimum. Yet, the developer is not restricted by the Kotlin rule environment, as aggressively as Rules DSL. So we will keep separate documentations and code examples for both audiences. Sounds ok?

As far as options go, I have removed those that might confuse beginners. I am inclined to keep function aliases like sendCommand(), updateState(), so that users can copy paste existing code from Rules DSL and get going.

Comparing Xtend and Kotlin on paper would lead anyone to same conclusions that you have presented. But what really matters is unforgiveness of the language at runtime for simplest of the issues, which I did encounter a lot. Thats why I gave up.

We all know OpenJDk is not same as Zulu JDK, and they are both not same as Oracle JDK. Comparing on paper they look exactly same. But we know what works and what not, from experience.

And also, there won’t be Xtend libraries as many as Kotlin. Xtend is a dead end language.

Talking about Python, there is a big difference between JSR223 languages(Python/JavaScript) and Kotlin/Scala/Groovy. The later group of languages can compile to JVM bytecode classes, that means they can call Java classes directly or vice-versa without having to have a “Generic Map to Native type conversion layer” in between. Thats why Python can never be as close to Java code as Kotlin/Scala could. Hard core Python fans should consider Home Assistant project.
At runtime, basically Kotlin and Java is practically indistinguishable. And apart from this, even annotations are inter-operable.

There should always be room for things other than OH. :slight_smile: With few exceptions this is just a hobby after all.

I took the weekend to reinstall some base molding, make some yogurt, try my hand at making biltong (too salty), building a cat tower, and sewing a new teddy bear for my son to take to school this Friday. It was a good weekend. :smile:

Pay attention to what matters. OH will be here when you come up for air.

It would help me out a lot if you reposted the link in a message like this. The original link gets lost in the thread, even more so when reviewing them on my phone, which is most of the time.

rule “Michael Wakesup” {

I like this as a way to make the Rule visually a single unit.

setTo and sendCommand are synonym, they send outgoing command

I don’t like the name “setTo”. It implies all you are doing is changing state. In many cases the Item (or Channel) may not actually change state in response to the command. That is the purpose of autoupdate=false, to handle the good situation where the device actively reports back its state promptly. In that case you would use autoupdate=false so the Item does not change state in response to the command but instead in response to the update that the device reports back.

There is also the issue that causes some people problems with received command triggered Rules (which isn’t supported in your Rules?) as the command Event goes on the Event Bus before the Item registry is updated with the new state (assuming that autoupdate=true, which is the default). This means the Rule starts running before the Item reaches the new state.

“sendTo” or something like that feels better.

sendCommand synonym is kept for backwards compatibility to rules DSL

I’d rather see you either just keep sendCommand or completely drop it instead of having two different commands that do the same thing.

If multiple ambiguos things/channels are found, this won’t do anything,

Log warnings at least? I’d rather not see something fail silently.

updateTo(AWAKE, “Our.Michael”)

The order of the arguments doesn’t matter? Everywhere else thus far shows the destination first then the command/state second.

other states: AWAY, ASLEEP, OUTSTATION, ATSCHOOL, ATWORK, any suggestions?

Don’t hard code these options and provide a way for users to define their own states.

// if current time is outside enabledAt and forbiddenAt, should the rule be enabled?

enabledByDefault { false }

I’m ambivalent about this. You already have this use case covered by defining your schedule in the enabledAt and forbiddenAt. This allows you to be sloppy with how you define your schedule but that increases the mental load on the developer later on when reviewing the Rule to understand when it will and will not run. For example, if I have it enabled Tuesday and Thursday, disabled on Wednesday but enabledByDefault set to true then I have to do a some mental manipulation in my mind to remember that even though I explicitly say Tuesday and Thursday, it is really every day but Wednesday. In this case it would be much more clear to leave off the enabledBy and just define the forbiddenBy instead of having a separate flag.

Maybe there is a use case I’m not thinking of but this feels like a feature that allows us to be sloppy now but have to pay for it later.

default 3.seconds

I’d rather have an unconstrainted default, or at least let us define unlimited. 3 seconds is a really long time for a lot of use cases.

periodically trigger rule, if not already trigged by triggerWhen conditions

So assuming we have a state trigger (which I still don’t like and think is more appropriate to place state in the enabledWhen clause or something like that) does this control how often the Rule retriggers? Or is this just a straight cron type trigger, or a combination of the two?

there could be multiple triggerWhen clauses.

I don’t like the loss of commands as a way to trigger a Rule. See above.

optional suppressWhen clauses to not trigger rule when certain conditions are met.

I don’t understand why we need separate clauses to define when the Rule should not trigger. Whether it is time based or state based or event based (does event based even make sense?) it doesn’t make sense to me that I’d need to define them and look for them in two different clauses. I’d like to see just one enabled clause, one fobidden clause, and one triggers clause. Or multiple of each of them but only have the three, not the five different clauses that exist right now.

continue when thing, item, channel not found or not ready. similar to bash’s set -e

Does this apply to whether or not the Rule runs, or when trying to use these unfound things in the Rule the Rule will continue to try to run?

handle collection based actions using LAMBDA expression

Can I assume Groups will still carry a collection of its members?

there could be multiple actions clauses

Do they execute in sequence or in parallel?

Over all I still think all the up front stuff(everything before actions) is more complex then it needs to be. I don’t like the loss of commands as triggers. And I still don’t like mixing Channels and Items in Rules.

Some of the things I see missing so far (I’m sure you have plans):

  • ability to call functions/methods from actions (i.e. how do I share code across Rules)?
  • how would a Rules library work (e.g. a State machine that I can use instead of needing to write my own)? In particular how would the users of the library identify those Items/Channels/etc for the library to operate on?
  • error handling

If we can get there perhaps. The proof will be in the pudding.

I am concerned that a number of concepts that are key to OH and how OH has always worked and how it is architectured are ignored or eliminated.

That doesn’t really buy much because if users are following the advice in the Docs and on this forum, they will be using MyItem.sendCommand(newState), not sendCommand("MyItem", newState.toString) so the copy and paste doesn’t really buy much. I’d rather see just one or the other.

From a practical perspective this reads as:

“Kotiln is beautiful. All you people who want to leverage a Python library that already solves your problem should just go and use Home Assistant because Python in ugly.”

I’m do not agree with that sentiment. I say Python not because I love it. I say Python because it lets OH leverage far more from the community than we can otherwise. Whether or not it is beautiful is besides the point.

Its a combination which also honors other restrictions as mentioned in updated docs.

// periodically trigger rule
// honors dontRetriggerWithin, forbiddenAt and suppressWhen conditions

In newbie doc, only this syntax will be documented

"Our.Michael".updateTo(AWAKE)

terminology is open. I really don’t have any preference of my own. We can keep original one from Rules DSL, if that avoids confusions.

Yes, of course. User needs to know there is issue with his intent being unclear.

Its being discussed in other thread.

The current spec is well thought around this. enabledAt, forbiddenAt and enabledByDefault, they are all needed in my own experience.

Maybe we can make default 3 seconds configurable at site specific file. It is is introduced to avoid server lock-up if user specifies a condition that always returns true. Otherwise, detecting dead-locks at static time is a sufficiently hard problem to solve. This basic barrier, 3 seconds solves it. If your rule does need a smaller retrigger delay, you can specify it. Like 1.nanos. Or even 0, no restrictions.

Very interesting question. Its doable as async calls or co-routines in Kotlin. I think we should it in parallel. :+1:

Absolutely, yes.

Aliases are introduced just for that. And there will be many other abstractions.

Kotlin’s exception handling would do. If something specific to us needs to be taken care of, then sure we will do it. Everybody please suggest!

I am no fan of Python but will still find a convenient way for those pieces of working/tested code to be leveraged, from ESH/OH/Kotlin. I would still suggest hard core Python fans to consider Home Assistant, you will find yourself at home.

I’d love to read an example where enabledByDefault adds anything beyond allowing you to define your times in the other two clauses in a sloppy way.

I’m OK with that. Though assuming that both are the same Rule and therefore likely to be using common resources, that means we need locking which opens a big old can of worms. Even experienced developers run into trouble with locks.

Though I can see this as a convenient way to have more than one “rule” that runs off of the same trigger. Though in my experience that is a fairly rare use case.

I worry about built in exception handling (switching back to the new non-technical user perspective) because they are often big, scary, and provide little information that someone unfamiliar with the actual source code cannot decipher. So I don’t think Kotlin’s exception handling by itself will be sufficient. What would be awesome is file, line, and column number and a single line that clearly explains to non-technical users what’s wrong and perhaps a suggestion of how to fix it. Honestly I’ve only ever seen error messages that useful in Ansible.

Well, most things that the actions would need will be vals, readonly objects. The actions on collections are generally not supposed to synchronize on shared state outside lamba. Thats disaster in combination with parallel invoke. By defaut Kotlin runs it sequentially. You can use async or co-routine syntax to parallelize these actions. We can’t modify forEach { // lambda } clause on Kotlin collections to mean something else in OH’s case. All Kotlin’s semantics we must honor. Otherwise Android guys will get confused when they write for OH. In our standard examples we will suggest and promote how parallel actions should be written, and when not to.

We need to work hard on this one. I know this problem. I visited this forum for a long time.

Very helpful suggestions Rich. Now we are talking.

So how can we persist a piece of data from one run of the Rule to another (global vals in Rules DSL) if only vals are allowed in actions? Do we now have to use an Item for all of that? In either case, it’s these variables that I’m referring to when I say shared resource.

I’m fine with only allowing access to vals inside lambdas like forEach. We have that now in the Rules DSL and 99% of the time a map/reduce can take the place of the variable anyway. But I’m concerned if we can’t have access to “global” vars in the actions clause.

Does it make sense to have a Rule scoped global?

Well, if the action on one item from list is supposed to update a shared counter that survives across rule invokes, then the writer should use atomic types. They allow for concurrent consistent access. We will document that.

If you mean certain global variable like syntax, then Java and Kotlin both got classes/singletons that you can use to share common state/knowledge across rules. And of course there are OSGI services, config services, JSON DB which are common source of truth for all rules.

Speaking as a new user of Rules and OpenHAB this topic is interesting, but very long and heated. Contrary to many who come to OpenHAB, I am a trained professional :wink: just not in Java nor in OpenHAB. So far I have been: impressed, frustrated, challenged and rewarded by OpenHAB and rules have been particularly challenging.

Since this topic is about rules, I’ll stick to feedback on that. My goal was to replace the control I had been doing in NodeRED with something in OpenHAB. In NodeRED, I was looking at the humidity in my basement (provided by an esp8266 running Tasmota and publishing to MQTT). I wrote a custom function (in JavaScript) to keep track of the time the temperature was above the set point and when it was above it for an extended period turn on the dehumidifier. It then kept track of how long the dehumidifier was below another point and would then turn off the dehumidifier. I figured using persistence in rules I could simplify the logic a lot. (I could have implemented this in NodeRED, but wanted to learn OH).

I am using VSCode and the OH extension, which helps, but I am not really familiar with what to expect, and I find it difficult to find a language specification for any of the DSLs. I see examples, but I never know if they are correct for the current version or not.

Anyway, I got a rule that looked fine and OH didn’t complain about and it did something. But, looking at the results, it was short cycling the dehumidifier (compared to what I expected). There was an error in the log that didn’t make any sense, but when I searched on it, I figured out I needed to use a map function (binary.map, but the color codes in VSCode don’t work like in the example, my text is all white, but the file fixed the error I was getting). So, now there were no errors, but the rule still wasn’t doing what I expected.

So, I said let’s add some logging. I looked at the example. Seemed simple enough I have used format strings for decades. I copied it and made the appropriate changes for my data. But, all I got printed was the format string itself. More searching.

I found an alternative way of doing it, several actually, but many of them were either incomplete or didn’t work. More searching and head scratching.

I finally had some success with 3 different ways of doing it, but 2 didn’t allow formatting for precision and the one that did is very picky about types. The error message it provided was less than helpful, but did give me a clue that it was about the type of the object that it didn’t like.

I finally got the logging sorted out and then I could see the problem. My average value was the same as my current value. So, more searching on persistence.

I then discovered that since I had set up rrj4 (or something like that) as the default persistence, it was not using influxdb. So, my request to get a 1 hour average was not going to work. Yes, I had configured it wrong, but that is because I had followed a tutorial on the internet that showed that way of doing it for simple persistence and then showed using influxdb for use with Grafana, but didn’t explain that maybe I shouldn’t (or should, still don’t really know) use both. The persistence method has a parameter for the service, so I told it to use influxdb and now things are working well.

My experience with OH has been like this. Incredibly frustrating, but just enough progress to get me to keep coming back for more.

2 Likes

This is the experience of many new users. Some will stick with it and others will drop, mostly straight away.
Persistence has two meanings in OpenHAB!

I agree, this can be very frustrating.

Another frustrating part, in general, is reading through long topics that contain several solutions that only apply to certain operating systems. For new OH users this can cause even more questions and doubt. Would be nice if topics, at least the longer ones, were separated by OS and setup being used. I know this doesn’t really apply to rules so much, but just saying.

So true…:grinning:

Sounds pretty typical. Since you are a trained professional I am not surprised that the Rules DSL is giving you the most frustrations. This is one reason I always recommend using JSR223 and one of those languages to those who already know how to program. Rules DSL has a number of deliberate limitations (no classes, no data structures, etc.) that make using it very frustrating.

I’m not sure you would need persistence for this unless you wanted the Rule to be robust enough to work even over an OH restart. A simple timestamp or perhaps Timers could be used. That’s besides the point though.

These will be your primary sources. The design pattern examples are almost always pretty reliable. Most of the tutorials are also pretty reliably correct.

Many of the other examples posted will have typos that VSCode should easily catch.

The Rules DSL really hasn’t changed in significant ways since OH 2.0 and even the changes from 1.8 to 2.0 are relatively minor. So for the most part any example you see on the forum should be mostly OK. VSCode should tell you mostly what needs to be changed.

If you see examples posted that include a bunch of import statements, they are almost certainly 1.8 or earlier version Rules. The list of things you need to change is documented here for such Rules.

At the release of 2.2 I think the Rules parser became a bit more strict so you might find some prior Rules that have what used to be soft error that now will not pass syntax checking. But the VSCode will tell you that as well.

You can use more than one. I use MapDB for restoreOnStartup and InfluxDB for charting.

It isn’t clear if you’ve found this or not, but in your Rule you can specify which database you want to use in your query by specifying the name as the last argument.

MyItem.averageSince(now.minusMinutes(10), "influxdb")

Anyway, OH is pretty complicated and has a lot of moving parts and all of those moving parts come together in the Rules. And everyone’s home automation is bespoke so it is really hard to simplify in some areas without eliminating some use cases needed by some users.

The error messages are better than they have been in the past but are by no means very meaningful in a lot of circumstances. One Rule of thumb that may help is when you see a null in an error message, it’s probably a type problem. The typing system in Rules DSL is frustrating because it is almost good enough. I use the following approach to minimize type problems:

  • Never use primitives unless absolutely necessary (there is some anecdotal evidence that it drastically increases Rules parsing at load time)
  • Avoid specifying the type of variables unless absolutely necessary; the Rules DSL seems better at this then I am
  • When using NumberItem, cast the state to Number, not DecimalType
  • When calling the sendCommand or postUpdate Actions, always use toString on whatever you pass to them
  • Use the sendCommand or postUpdate methods where ever possible
  • Always test that an Item doesn’t have a NULL state before casting it or trying to use it in calculations

These should vastly limit if not eliminate type problems.

Not sure if any of this helps. Thanks for posting!