Structures or Objects to group Items?

My Items and Rules are getting to the stage of getting very messy and indirect. It seems like you have to use cheats and workarounds to get anything reasonable done. So I think I might be doing something wrong. One area I’m having a big problem is around Items. It feels like I need to be able to create a structure or object, where I can group items together.

Here is kind of what I have or want

Dimmer BedroomLight "Bedroom Light" (gBrightness, gBedroom)
Color BedroomColor "Bedroom Color" (gColor, gBedroom)
Color BedroomProxy (gProxy, gBedroom)
String BedroomProxy_rgb (grgb, gBedroom)
Color BedroomOnColor (gOnColor, gBedroom)
Color BedroomOffColor (gOnColor, gBedroom)
DateTime BedroomTimeStamp (gActionTime, gBedroom)
DateTime BedroomOffTime (gOffTime, gBedroom)

Now I want that setup for every room. So that’s lots of copying and pasting. It would be great if I could create a light structure or object with those properties once and then just apply that to all my lights.

myLight {
Dimmer state
Color stateColor
Color offColor
.....
}
//define it just with
myLight Bedroom
//useage
Bedroom.sendCommand('state="OFF"') //psudo syntax

At the moment I have strange rules that are based on the actual name of the rule. e.g. rgb rules act directly on manipulating the objects name and passing that string, etc.

Is there anyway to create or organise Items without having to copy,paste and rename all the properties for each light. Also to be able to link the items through this structure without having to go indirectly by groups.

Look at the Home Builder add on (I think it is under misc in p as PaperUI). It will let you
More easily define this sort of structure but ultimately you will have reach of these items separately defined. There is no way to do what you describe.

And groups are the best way to create collections of items that are related to each other. You could do something through the rest API perhaps but you are trying a dozen one line definitions for groups for dozens of lines of http call and String parsing. Not a fair trade in my opinion.

You might be able to do something interesting in JSR223 but I don’t have enough experience there to help. Not that this might eleviate the need for groups but you will still have to define each item individually.

You could write a preprocessor and put some structure in your items file that gets replaced with the items definitions.

I was actually just looking at JSR223, jython looks pretty cool. You could made an object and then convert that to a string using json parse or something. Then just store that object as an Item. So you just need two Items, one with all the information and that the rules use and the other just raw direct control of the item. See my post here Xtend Scipts vs JSR223?

The problem is how does that work with the UI’s? How does that work with Persistence? How about Alexa/Google Assistant (or anything else that depends on tags on Items) integration?

You can only put Items on your Sitemap or Habmin and you can only save the States of Items to persistence. You can only add tags to Items. And you likely don’t want to just use the “raw direct control” Item in all of these cases, at which point you are back in the position of needing to define separate Items to represent everything.

This approach will work if you don’t put the Items on your UI, don’t depend on persistence, and don’t expose the Item to be controlled externally (e.g. Alexa). For some, that can render OH useless to them.

This may be perfectly acceptable to you so I’m not trying to talk you out of going down that path. But I cannot support this approach as a general recommendation to OH users at large.

I was going to say that Alexa/Google and the UI don’t need to see any of that apart from the raw device. They are items such as

  • A timer to turn off the light seconds after last motion.
  • The color to turn the light on when I turn it on.
    *etc.

But thinking about it maybe what I need to do is write a decent interface so that rather than writing the objects to a string it creates an items file with all the items on first use. Essentially make a python wrapper for objects made up of multiple items.

Which is what Home Builder does.

I can’t get Home-builder to let me define objects. If you go up to my first post you see I want to define my lights like

Dimmer BedroomLight "Bedroom Light" (gBrightness, gBedroom)
Color BedroomColor "Bedroom Color" (gColor, gBedroom)
Color BedroomProxy (gProxy, gBedroom)
String BedroomProxy_rgb (grgb, gBedroom)
Color BedroomOnColor (gOnColor, gBedroom)
Color BedroomOffColor (gOnColor, gBedroom)
DateTime BedroomTimeStamp (gActionTime, gBedroom)
DateTime BedroomOffTime (gOffTime, gBedroom)

Then I just want home builder or whatever to use that structure for all my lights. Homebuilder is really basic and doesn’t even have Color lights. But there doesn’t seem like there is any room for custom object that aren’t switches.

I’m pointing you to Home Builder for an example of how to do it. I’m not trying to get you to use it.

The only thing I can add is that your particular use case is somewhat unusual. I suspect it is the rare case where people have the exact same items in every room. This is probably why nothing like this has been proposed before.

If you get something working and it doesn’t go too far outside the bounds of the OH architecture (e.g. requires JSR223 to work) I’m sure it would be welcomed if you submitted it as an addon. Home Builder started as a Python script.

For those who are trying to use OH in a hotel type context would find a tool like you are describing quite useful.

Ooh, I’ll have a look round github and see if I can work off the Home Builder base

Edit: Do you know where it is. It’s under UI in Paper UI but most of the UI isn’t in the UI folder on github

I found it. Ahh it’s js. I think I’ll rewrite it from scratch in python or any other language,

That would exclude it’s ability to be merged as part of the OH baseline though. Kuba originally wrote it as Python too and rewrote it as you see.

I do something similar using a hashmap in my rule. It has entries for each light, the lux level that the light should be triggered at if the area is active (motion, doors, locks, etc.), and the brightness level based on time of day. I also use this for turning on music in rooms, but I have not yet incorporated color. But I plan to, as I have some LED strips connected to the wifiled binding. I want to put together a tutorial on how I am doing this, because I am also using a single rule and lambda for all of my lighting, along with an interesting approach for using groups to mark rooms as active. I’ve been held up posting this, as I rewrote it in jsr223 Jython, but I may have found a bug. I’ll set a goal for posting the Rules DSL version by the end of the weekend. Maybe this approach would work for you.

That would be great. It would be nice to see how someone else has approached something like this.

Posting both would be awesome! There are not many examples for people to use to compare the two approaches (JSR223 and Rules DSL) side be side. It was always a goal of mine to convert my Rules to JSR223 just to be able to have these side by sides and to add a JSR223 section to the DPs, but I’ve not gone past “hello world” in proving that I can get JSR223 to work. I’ve not had the time or patience yet to look into the code to answer questions I can’t find answers to in the docs.

Do you hard code the HashMap, populate it dynamically, or something else? If hard coded, did you consider looking for a way to load it from file so you can provide a little bit of separation between the Rules and the configuration, which is what it sounds like this HashMap contains?

I’ve been thinking of ways to do this (DP idea) but haven’t encountered a compelling use case yet. Some approaches I’ve been thinking about (all Rules DSL):

  • Use the Java Properties Object to load the parameters from file
  • Use a .map file and transform Action similar to Design Pattern: Human Readable Names in Messages
  • Use a file reader or executeCommandLine(“cat properties.txt”, 2000) and parse out the values

Since you’ve actually coded the HashMap up do you have any thoughts?

I’ll do that. I spent a lot of time trying to find examples and make sense of jsr223. I started by converting all of my Minimote (zwave scene controller remote) DSL rules, and then jumped into my Mother of Mother of All Light Rules rule. :slight_smile: Surprisingly, I didn’t see any performance benefit from it (both ran ~20-30ms), and with the bug I ran into, I stopped converting to jsr223. I plan to put the code for my post into Github, so I’ll include both versions, but the jsr223 one doesn’t currently trigger (I had been testing by changing an item in Karaf). Another hurdle with understanding the info in the forum on jsr223, is separating the default ESH API and the modules and scripts from JSR223 Scripters. When I’m looking for something to do, I’ll start posting some jsr223 conversions in your DPs too. Who could create a new categpry in the forum for jsr223, and subcategories Jython, JS, Groovy, etc.?

I’ve actually done it a few ways, but currently it is just hard coded as a global. I would really like to move from using the hashmap to item metadata, but I haven’t yet gotten a response to that question. There’s a sample of the hashmap in that thread. I do file operations for TTS (I used pico2wave, but currently use flite), so it wouldn’t take much for me to pull it in from a file. I had actually thought of the possibility of doing this so that I could make a site to modify the values. I just haven’t had a need because changing values is rare and easy to do in the rule file.

The gist of it is to have gArea_Trigger and gArea_Action groups, and a single rule that will turn on/off lights, speakers etc. for the items in gArea_Action, depending on the state of its corresponding gArea_Trigger. I was able to remove over 500 lines from my rule files with this, and performance is the same to very slightly higher then previously. The tricky part is figuring out the group logic!

The rule is a little simpler, but I prefer the FileReader version because building the XML would be a headache.

import java.util.HashMap
import java.util.Properties
import java.io.FileInputStream

rule "Test"
when
    Item Virtual_Switch_1 received command
then
    var Properties prop = new Properties
    val String propFileName = "/opt/openhab2/conf/test/test.xml"
    prop.loadFromXML(new FileInputStream(propFileName))
    var HashMap<String,String> test = new HashMap<String,String>
    for (String key : prop.stringPropertyNames()) {
        val String value = prop.getProperty(key);
        test.put(key, value);
    }
    logDebug("Rules","Test: [{}]",test.toString)
end

Where test.xml contains…

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <entry key="key1">value1</entry>
    <entry key="key2">value2</entry>
    <entry key="key3">value3</entry>
</properties>

That DP is confusing for me because i would use item.label for a friendly name. :stuck_out_tongue_winking_eye:

import java.util.List
import java.io.File
import java.io.BufferedReader
import java.io.FileReader
import java.util.HashMap

rule "Test"
when
    Item Virtual_Switch_1 received command
then
    logDebug("Rules","Test: Start")
    val String filePathString = "/opt/openhab2/conf/test/test.txt"
    val File filePath = new File(filePathString)
    if (filePath.exists) {
        var HashMap<String,String> test = new HashMap<String,String>
        var BufferedReader in = new BufferedReader(new FileReader(filePathString))
        var String line = ""
        while ((line = in.readLine) !== null) {
            val List<String> parts = line.split(",")
            test.put(parts.get(0), parts.get(1))
        }
        in.close
        logDebug("Rules","Test: [{}]",test.toString)
    }
    else {
        logDebug("Rules","Test: file does not exist!")
    }
    logDebug("Rules","Test: End")
end

Where test.txt contains…

key1,value1
key2,value2
key3,value3

:+1:

Good question. @ThomDietrich used to have this power. He hasn’t been around for awhile so I don’t know who would have that ability. I’d say in the mean time let’s just use Tutorials and Examples and tag with JSR223. They can be moved later if needed.

Properties supports plain old map files. No need for XML and XML is not what I had in mind. So test.properties would be:

key1:value1
key2:value2
key3:value3

You can use = if you prefer. I don’ think , is supported like your last example though.

No one wants to write XML by hand. Yuck.

Just use prop.load(new FileInputStream(propFileName)

But for your multidimensional example you would have to use namespace hierarchy:

Morning.Low_Lux_Trigger:20
Morning.Level:1
Day.Low_Lux_Trigger:50
Day.Level:55
...

Since Properties is already a HashMap, why do you put the contents of it into another HashMap?

Your Rule could be:

import java.util.Properties
import java.io.FileInputStream

val prop = new Properties

rule "Test"
when
    Item Virtual_Switch_1 received command or
    System started // presumably one would want to load this at startup
then
    val String propFileName = "/opt/openhab2/conf/test/test.properties" // the name of the file really doesn't matter
    prop.load(new FileInputStream(propFileName))
    logDebug("Rules","Test: [{}]",prop.toString)
end

// In your Rules
    val value = props.get("key1")

But my Item labels that I want to display on my sitemap bear little to no resemblance to the name I want to use in my alert messages. So on my sitemap I might use:

image
but want my alert to read:

“The following doors are open and it is after dark: garage door 1, back door, and front door.”

I can’t use the Item label for both. And it’s a DP. That one use case (i.e. converting Item names to friendlier Strings) is just an example. It isn’t the only use case the approach is useful for.

If we apply the DP to this use case we can trade some inefficiency (i.e. the file gets loaded every time you need to access a value) for fewer lines of code.

// In your Rules
    val value = transform("MAP", "test.map", "key1")

If you are pounding the file you will probably run into performance problems but if you are not, in my experience, I’ve seen no measurable performance hit. Though like I often say, performance is almost never a problem in the home automation space.

The reason why I mentioned cat is because that rule could collapse down to:

import java.util.Map

val Map<String, String> test = newHashMap

rule "Test"
when
    Item Virtual_Switch_1 received command or
    System started
then
    logDebug("Rules","Test: Start")
    val String filePathString = "/opt/openhab2/conf/test/test.txt"
    val props = executeCommandLine("cat " + filePathString, 1000).split("\n")
    props.forEach[ line | 
        val parts = line.split(",")
        test.put(parts.get(0), parts.get(1))
    ]
    logDebug("Rules","Test: [{}]",test.toString)
end

Obviously I’m missing some error handling and it would only work on a machine that has cat (sorry Windows users). I would add some in when I write it up as a DP.

Though maybe this is just a special case for the Human Readable Names DP and not a DP unto itself.