Update Group status on sitemap?

With regard to my tilt, turn (or open | ajar | closed) status on windows
I have a group item on the sitemap indicating the window status; however, it supports OR, but not a tri-state… now what?

I thought – even if not perfect, that it would pick up at least on “open” (does open lower case matter for: Group:Contact:OR(OPEN, CLOSED) gWindows "Open Windows [(%d)]" <contact> (gAll)

How do I get this group item to count open | ajar windows?

The Group can’t be a Contact for tristate, it will have to be a Number. Then you can use SUM. Since OPEN is represented as a 1 internally the Group would be set to the number of open windows. NOTE: Assumes all members are Contacts.

Group:Number:SUM gWindows "Open Windows [(%d)]" <contact> (gAll)

NOTE: Assumes all members are Contacts

If, on-the-other-hand, you want the Group to show OPEN if one or more of the members are OPEN then what you have will work. The Group will be OPEN is one or more members are OPEN. NOTE: Assumes all the members are Contacts

However, if you are using a String to represent the state of the Windows in your members it becomes much more tricky and I’m not entirely certain you can get a count of open windows using a Group. You will have to use a proxy Number Item and a Rule to update that Item whenever the Group receives an update.

rule "Open windows"
when
    Item gWindows received update
then
    val numOpen = gWindows.members.filter[w|w.state.toString != "closed"].size
    OpenWindows.sendCommand(numOpen)
end

If you only care that at least one of the windows is open or ajar but don’t need a count the following might work. I’ve never tried to use Group aggregation using Strings before.

Group:String:AND("closed", "open") gWindows ...

The AND("closed", "open") will cause gWindows to be set to “closed” if and only if all of the members are set to “closed”. If one or more of the members are “ajar” or “open” then gWindows will be set to “open”.

2 Likes

Thank you kindly!

What I gather:

  1. since windows can have a tri-state, a CONTACT will not do it
  2. (your) other suggestion (elsewhere) was to use a STRING; but this would not help in this case, as it cannot be SUMmed up.
  3. if it where a NUMBER, the states are represented as 0|1|2; SUMming them up would lead to an incorrect number of open windows, because ajar being 2, adds two ‘open’ windows to the count :slight_smile:

This is very interesting in many ways:
a) even if I can count properly; what if the system restarts, how do I know which state a window has?
b) using MQTT also sends its message once (and does not care about restarts; where persistence comes in with mapDB)
c) an option would be to count CLOSED and subtract it from the number of windows.

… which is what you suggested here:

I will experiment with that…

I never imagined it would get so tricky.
I am glad I am trialling this ahead of the actual build.

I solved this problem by setting up a topic that the sensors subscribe to and when any message gets published to that topic the sensors report their current state. when OH starts up it sends a message to that topic and the sensors report their current status on their usual topics.

Alternatively the sensors could report their current state periodically whether or not there was a change. Then OH will never be more than the reporting interval out of date.

There is an MQTT quality is service setting that ensures exactly once delivery. I think this would queue up messages sent to the OH topic while OH was down. It might be worth a bit a research.

1 Like

Like I said: I am glad I am trialling this before actually implementing this or any of it. :slight_smile: Imagine doing this while the house is “live” – what a pressure this will be! :frowning:

This is why I am going down the DIY path… I can modify ‘things’ in the early stages to do easily (or easier) what I want to achieve.

Since the MQTT stack is loaded onto the Arduino, making it listen to incoming messages will not be difficult.

I don’t have to imagine it. Most of us don’t have the luxury of building a house and have to deal with all of this stuff live (my house is 20 years old). I think that may be why I and a lot of the other users are so cautious about maintaining the old way of doing things and wall switches and such. As we are trying to figure all of this stuff out we have a backup that still works. Otherwise we would be sitting in the dark furiously trying to code our way out of what ever mess we made of our home automation.

Getting rules and Groups and such to work is actually pretty tough, particularly when you start hitting edge cases and weird behaviors. And I can say, when they are not working the pressure is indeed pretty high.

But along the way I’ve managed to learn quite a few tips and tricks that I hope will help the community. The most useful ones I’ve posted to the Design Patterns thread that I keep sending everyone to.

1 Like

I am still of the opinion that this forum can be very happy for having you!
Thank you again for your work, and your quality contributions. I am sure there are others, but I am fan of these…

… which is still persistent in my browser window :slight_smile:

I know… I know… :blush:

This is doing my head in… it does not count, actually, I can’t see the value (it may count – no it doesn’t, because I do not see it INFO logging)

sitemap: Text item=OpenWindows
items:
Number OpenWindows "numOpenWindows [%.0f]"

String Window_House_Kitchen "Kitchen Window [%s]" <contact> (gHouse_Kitchen, gHouse_Windows, gHouse_Veranda) {mqtt="<[mymosquitto:ArgyleCourt/House/Kitchen/Window:state:default]"}

Group:Contact:OR(OPEN, CLOSED) gHouse_Windows "Open Windows [(%d)]" <contact> (gAll)

rules:


rule "Number of Open windows"
when
  Item gHouse_Windows received update
then
  val numOpenWindows = gHouse_Windows.members.filter[w | w.state.toString != "closed"].size
  OpenWindows.sendCommand(numOpenWindows)
  logInfo("OpenWindows", numOpenWindows.toString)
end

Would: var int numOpenWindows = [...]
be the same as: val numOpenWindows
If so, why have both?
One more correct than the other?
(Personally I like the var int more, as it is more like standard programming.)

Neither is more correct than the other, they mean different things. val denotes a variable that cannot be reassigned. In C/C++ this would be equivalent to making a variable a const. In Java it is equivalent to making it final. Using val or its equivalent in other programming languages is a form a defensive programming. It basically lets you state up front that a variable cannot be reassigned and if you forget or have a bug where it does get reassigned the program will throw an error and Designer will underline it in red.

Furthermore, in certain lambdas like in a forEach one can only access vals defined outside the lambda, not vars.

When you have a constant or a val that only gets assigned once (as in this case), or a data structure like a HashMap or StringBuilder where the structure isn’t reassigned but the contents might be, val should be your choice (IMHO). It is a good habit to get into.

var denotes a variable that can be reassigned. This is used when you are using a variable to hold intermittent values that gets built up over time, a flag that could change later in the rule, and any situation where the variable is intended to be reassigned.

A var defined in a rule or globally cannot be accessed within certain lambdas like forEach (it can be accessed in a Timer). This does provide a challenge because you can’t for example, run a forEach loop and build up a count or something like that into a variable defined outside the forEach.

The int part is optional. The Rules DSL is a weakly typed programming language (others include JavaScript, Python, Ruby). As such, the type of a variable is determined at runtime. Therefore you are not required to declare the data type of your vars and vals. However, the Rules DSL is also running on and deeply integrated with Java which is strongly typed so, unlike some of the other weakly typed languages, the Rules DSL does let (and in a few places requires such as lambda definitions or vars which get initialized to null) you indicate the type for your vals and vars.

Unfortunately this is one area where the Rules DSL has some weaknesses. Some problems I’ve encountered:

  • the data type of mathematical calculations is BigDecimal, not a primitive like int or float
  • if for some reason the Rules DSL cannot convert the actual data type to the data type that is needed it throws a NullPointerException
  • there are bugs that pop up periodically that causes calls to the postUpdate or sendCommand actions that leave the vales in their default dataType (e.g. BigDecimal for a Number Item) fails with a NullPointerException whereas sending the same variable using .toString works and/or sending the value using the MyItem.postUpdate, MyItem.sendCommand will work.

Given these problems, there are times where indicating the data type can be helpful, particularly when needing to working around BigDecimal, but in most cases the data type is pretty obvious.

I didn’t include the data type in this case because I didn’t think it was required, though not that I look at it more closely, if .size really does return an int then the numOpenWindows.toString wouldn’t work because there is no toString on a primitive.

Are you seeing any errors in the logs?

Try adding a logInfo as the first line of the rule to verify it is getting triggered.

Lets convert the result of the .size to a Number:

val Number numOpenWindows = new Number(gHouse_Windows.members.filter[w | w.sate.toString != "closed"].size)

Move the logInfo that prints out the value of numOpenWindows before the sendCommand so we can see if there is an error with sending the command verses the counting of the number of open windows.

Try sending numOpenWindows.toString fi the sendCommand seems to be failing.

There is no such thing as a “standard programming” :stuck_out_tongue:

But if defining the data type feels better please do it. It is actually another form of defensive programming in this context. However, the major drawback will be that you will encounter some data type errors in your rules that would not be errors if you let the Rules DSL figure out and convert data types around for you.

1 Like

Thank you kindly for your huge post… I am still digesting it… and will get back…
Am I just too stupid to find this stuff myself, or is this actually written down somewhere so I can read up on this?

OK… made all the changes suggested… that is:

rule "Number of open windows"
when
  Item gHouse_Windows received update
then
  val Number numOpenWindows = new Number(gHouse_Windows.members.filter[w | w.state.toString != "closed"].size)
  logInfo("OpenWindows", numOpenWindows.toString)
  OpenWindows.sendCommand(numOpenWindows.toString)
end

… with the variation of OpenWindows.sendCommand(numOpenWindows)

Neither one logs INFO, or updates the number.

I did add after ‘then’:

  logInfo("OpenWindows", "numOpenWindows.toString")

to see if I get a log entry… nope. Meaning this rule does not get triggered?!

It is written down to some extent but:

  • The info is scattered between various wiki pages and third party sites (in this case var and Val is discussed on the Xtend documents, link on the Rules wiki page)
  • Low level detailed discussion of something like this usually receives one or two sentences in docs when they are treated at all

Most of the above primarily comes from my 15+ years as a professional programmer coupled with a computer science degree with a little security engineering thrown in and many hours of banging my head trying to get things to work in rules. Rarely does a concept like the difference between Val and var get this much text devoted to it in docs because often those doc’s are written by programmers for programmers and this sort of thing is generally just understood. My intent is to eventually write up an extensive Rules DSL section for the OH 2 docs which will include most of the postings I’ve made like this.

But right now time to sit in front of a computer is in short supply so I’ve not done much yet.

1 Like

That is what that means. We are closer to the problem. Are you certain the
item you are changing to trigger the rule is a member of the group? No
typos in rule or items file on the group name? I don’t think it will throw
an error if you use a nonexistent item as a rule trigger or assign an Item
to a nonexistent group.

Are you certain your item is getting updated? You should see it being
updated in the events log.

1 Like

Yes… I am not alone (Sad as it is) :slight_smile:

Yes.
2016-07-30 13:53:29 - Window_House_Kitchen state updated to OPEN 2016-07-30 13:53:29 - Window_House_Living state updated to OPEN 2016-07-30 13:53:29 - Window_House_Bedroom_South state updated to OPEN 2016-07-30 13:53:29 - Window_House_Ensuite state updated to OPEN 2016-07-30 13:53:29 - Window_House_Bathroom state updated to AJAR 2016-07-30 13:53:29 - Window_House_Plant_Room state updated to AJAR

String  Window_House_Kitchen            "Kitchen Window [MAP(en.map):%s]"    <contact>    (gHouse_Kitchen, gHouse_Windows, gHouse_Veranda)    {mqtt="<[mymosquitto:ArgyleCourt/House/Kitchen/Window:state:default]"}
String  Window_House_Living            "Living Window [MAP(en.map):%s]"    <contact>    (gHouse_Living, gHouse_Windows)                {mqtt="<[mymosquitto:ArgyleCourt/House/Living/Window:state:default]"}
String  Window_House_Bedroom_South        "Helga Window [MAP(en.map):%s]"        <contact>    (gHouse_Bedroom_South, gHouse_Windows)            {mqtt="<[mymosquitto:ArgyleCourt/House/Bedroom_South/Window:state:default]"}
String  Window_House_Ensuite             "Ensuite Window [MAP(en.map):%s]"    <contact>    (gHouse_Ensuite, gHouse_Windows)            {mqtt="<[mymosquitto:ArgyleCourt/House/Ensuite/Window:state:default]"}
String  Window_House_Bathroom            "Bathroom Window [MAP(en.map):%s]"    <contact>    (gHouse_Bathroom, gHouse_Windows)            {mqtt="<[mymosquitto:ArgyleCourt/House/Bathroom/Window:state:default]"}
String  Window_House_Plant_Room         "Plant Room Window [MAP(en.map):%s]"    <contact>    (gHouse_Plant_Room, gHouse_Windows)            {mqtt="<[mymosquitto:ArgyleCourt/House/Plant/Window:state:default]"}

Group:Contact:OR(OPEN, CLOSED) gHouse_Windows "Open Windows [(%d)]" <contact> (gHouse)

On the UI, the window open number changes to 4, which is at present correct, as it does not understand ‘ajar’ yet.

Ok, the problem is all of your Item’s are a String but your group is a Contact. So when the code that tries to run the OR tries to roll up all the values it is looking for Items with a state of OPEN. But OPEN is not the same thing as “OPEN” (the OPEN that can be a state on a Contact is an OpenClosedType where as “OPEN” is a String).

In most cases the errors language and bindings and such is able to figure out how to convert the String to an OpenClosedType for you. Unfortunately this is not one of those cases.

I think it will work if you convert the group to a String:

Group:String:,OR("OPEN", "CLOSED")

Note, case matters in this case too.

1 Like

The groups with Group:Contact:OR(OPEN, CLOSED) do count.
I think the problem of tri-state (open|closed|ajar) remains…
Which is related to:

  1. the rule you’ve tried to sort out
  2. a general problem I encountered where the rules (despite valid conditions) are not triggered at all. on the Linux system, while logInfo seems to work differently on Windows.

Is this only me having problems like this (logging) or is it really that hard to understand the syntax of this beast?

BTW: the change to String did not change anything (which is almost expected due to the tri-sate possible, and ajar not being read as open)

When you have confirmed that the items are changing state but your right
isn’t triggering the possible causes are:

  • Typo in group name in the trigger. Double check that the item name in the
    trigger exactly matches, including case, the way it is defined in the items
    file. Designer will not see this error.
  • Typo in items where there is a typo in the name of the group it is a
    member of. Make sure the group banned exactly match the way it is defined.
    Designer dues not pick up this error either I think.
  • Syntax error in the items file which prevents openHAB from losing all of
    the defined items.
  • Syntax error in rules file which prevents openHAB from loading all of the
    rules in file. A common error is trying to intersperse global variables and
    rules (global variables must be at the top).
  • Misunderstanding about how a ride trigger works. The most common here is
    expecting a changed trigger on a group to trigger the right when ever any
    member of the group changed. This is incorrect, such a row will only
    trigger when the group’s rollup state changes ( e.g., if all switches are
    off and one turns on the group triggered rule will fire because the group’s
    state changed, but the rule will not fire when the second switch turns on
    because the group’s state is already on).

With all the support I’ve done on this forum, rules not triggering has come
down to one of these errors. Some are obviously not applicable in your case.

Make sure you are using Designer. Watch the log for errors, particularly at
startup. Move the rules around in your file to see if, for example new
Rules note stop firing. Reduce rules and items to their simplest and build
up from there again.

It isn’t just you. Sometimes people encounter errors that are very
difficult for us on the forum to help trouble shoot. Most of the time
though this sort of thing doesn’t happen often.

Finally, while I’m an advocate for the Rules DSL, some people do better
using the JSR233 binding and coding rules in JavaScript or Jython. Though
the events that trigger errors are the same so it probably wouldn’t help in
this case.

1 Like

First update… (I am sure I find more issues)…

Removed all rules I can do without! – This fixed the issue with logInfo as well as sending mail!

Thank you soo much Rich! I can’t say it often enough… spent 8 hours yesterday and 3h today trying to figure this out.

First result:

11:12:34.607 [DEBUG] [m.r.internal.engine.RuleEngine:305  ] - Executing rule 'ATA is offline'
11:12:34.688 [INFO ] [.openhab.model.script.gNetwork:53   ] - Rule: ATA is offline triggered
11:12:38.233 [DEBUG] [m.r.internal.engine.RuleEngine:305  ] - Executing rule 'ATA is offline'
11:12:38.245 [INFO ] [.openhab.model.script.gNetwork:53   ] - Rule: ATA is offline triggered
11:12:38.265 [DEBUG] [m.r.internal.engine.RuleEngine:305  ] - Executing rule 'ATA is offline'
11:12:38.270 [INFO ] [.openhab.model.script.gNetwork:53   ] - Rule: ATA is offline triggered
11:12:38.290 [DEBUG] [.p.m.i.MapDBPersistenceService:146  ] - store called for Network_ATA
11:12:38.300 [DEBUG] [.p.m.i.MapDBPersistenceService:167  ] - Stored 'Network_ATA' with state 'OFF' in mapdb database
11:12:38.307 [DEBUG] [m.r.internal.engine.RuleEngine:305  ] - Executing rule 'ATA is offline'
11:12:38.318 [INFO ] [.openhab.model.script.gNetwork:53   ] - Rule: ATA is offline triggered
11:12:42.444 [DEBUG] [nhab.action.mail.internal.Mail:156  ] - Sent email to 'blah@gmail.com' with subject 'Rule: Item Network_ATA'.
11:12:43.013 [INFO ] [.openhab.model.script.gNetwork:53   ] - ATA is OFF (state)
11:12:44.494 [DEBUG] [nhab.action.mail.internal.Mail:156  ] - Sent email to 'blah@gmail.com' with subject 'Rule: Item Network_ATA'.

Second result

The counter for open windows now also works!

/* ***** determine number of windows either open or ajar ***** */
rule "Number of open windows"
when
  Item gHouse_Windows received update
then
  logInfo("OpenWindows1.rule", "testing, gHouse_Windows received update")
  val numOpenWindows = gHouse_Windows.members.filter[w | w.state.toString != "CLOSED"].size
  logInfo("OpenWindows2.rule", numOpenWindows.toString)
  OpenWindows.sendCommand(numOpenWindows.toString)
end
2016-07-31 11:50:08.362 [INFO ] [c.internal.ModelRepositoryImpl] - Refreshing model 'max01.rules'
2016-07-31 12:33:44.906 [INFO ] [model.script.OpenWindows1.rule] - testing, gHouse_Windows received update
2016-07-31 12:33:45.870 [INFO ] [model.script.OpenWindows2.rule] - 5

It shows 3 as open, but numOpenWindows shows 5 (now including the AJAR ones too).

All I have to do is clean up the UI… :slight_smile:

Third result

Yes, made this mistake (which I would not do in any other language – why here? I do not know.

I have in the meantime added one rule after the other back into the .rules file, and tested them; all good. Constants all over the shop was the culprit in this case.

Glad you made some progress. I’m also glad you were able to parse through all that autocorrect nonsense. It isn’t always easy to edit my posts when the toddler is demanding attention.

It sounds like you probably had a subtle syntax error in one of the unrelated rules you removed. Maybe a mismatched paren, bracket, or curly bracket.

1 Like

Well, Smarty Pants :slight_smile:

You nailed it … yes I took a break this arvo; however, could not resist to try your suggestion:

Group:String:AND("CLOSED", "OPEN")  gHouse_Windows  "Open Windows [(%d)]" <contact>

Again, thanks a lot for your input! I certainly learned a lot… and am grateful for that.
This works… and the rule to do the counting is no longer required – but kept in my rule stash :wink: