[SOLVED] Proper Syntax/Use of Item.lastUpdate?

The answer is, I obviously don’t know how lastUpdate is supposed to be used. The question is, how is it supposed to be used?

The rule parses OK when I save the file. B when the rule is triggered, the following error is logged:

‘lastUpdate’ is not a member of ‘java.util.concurrent.CopyOnWriteArrayList’

rule "My rule"
        when
            Item gItem received update
        then
            val Switch first_item = gItem.members[0]
            val DateTime item_updated = first_item.lastUpdate.toDateTime

            logInfo("rules_test", "Item " + first_item.name + ", Updated at " + item_updated.format("%1$tm/%1$td/%1$tY, %1$tH:%1$tM"))
end

Mike

P.S. The ultimate goal of the rule will be to find the item in the group that has the most recent update and take and action based on that item’s state. I read somewhere about using a filter (syntax similar to retrieving the number of ON items - numON = gItem.members.filter[SwitchItem sw|sw.state == ON].size) but using a sort of the members to have it return the newest item. From the posts I read, that seems to return inconsistent results. Besides, I’m even more clueless on how that syntax would look. Thus I have resorted to the old fashioned “find the newest” loop.

OK, the issue isn’t lastUpdate. It doesn’t matter what property I try to use (.state, .name, .lastUpdate, …) it throws the same error. So, I’m guessing my syntax to assign first_item as the first member of the group by using members[0] is not proper syntax. I have never written a line of Java code so I’m trying to decipher the syntax from examples on this forum… and obviously failing.

Any help is appreciated!

Regards.

Mike

P.S. Huge learning curve! About to can the whole home automation hub experiment I’m afraid (for assorted many reasons where things are not working as they ought to… apparently though I have everything set up properly). Given the instability I’m facing and the difficulty in getting even straightforward logic to execute… disheartened comes to mind!

There is no “first item” in a group, and there is no pointer to select a single item. Instead, you have to pass through the list of items:

rule "My rule"
when
    Item gItem received update
then
    gItem.members.forEach[i|
        logInfo("rules_test", "Item {} Updated at {}", i.name, i.lastUpdate.toString)
    ]
end

Take a look at Design Pattern: Working with Groups in Rules

Hi,

val Switch first_item = gItem.members.get(0)
val DateTime item_updated = first_item.lastUpdate.toDateTime

The problem is that the items in a group are not in a fixed order as @Udo_Hartmann said. So the get(0) will end up picking a random item depending on the order the items were loaded the the group hashmap at the start of openHAB. The link he has referred to is very good. Highly recommended.

Udo & Vincent,

Thanks for your guidance! Through other questions, Rich had directed me to a number of his design pattern posts but I had not stumbled on the one on handling groups. Thanks for that link.

I had found some other of Rich’s posts which used “forEach” style syntax, e.g.,

    val numON = gItem.members.filter[s|s.state == ON].size

I had tried to adapt that… to no avail. That’s when I resorted to statements using the [0] syntax trying to retrieve a member of the group (to initialize a variable to begin my looping logic). Nevertheless, Rich’s Groups Design Pattern is exactly what I needed to retrieve the “newest” Item.

Question: Can I not reference .name and .lastUpdate outside the context of a “forEach”? I’m slowly coming to that realization. In other words, can I refer to newest_item.name or newest_item.lastUpdate?

val newest_item = gItem.filter[ s| s.lastUpdate("mapdb") !== null ].sortBy[ lastUpdate("mapdb") ].last as SwitchItem
if (newest_item.name == "Mike") then do_something
if (newest_item.lastUpdate == yesterday) then do_something_else

Thanks.

Mike

Yes you can do that

The reason I asked was that I get this error message in the log:

‘name’ is not a member of ‘org.eclipse.smarthome.core.library.items.SwitchItem’;
‘lastUpdate’ is not a member of ‘org.eclipse.smarthome.core.library.items.SwitchItem’;

Try removing the as SwitchItem at the end of your group filter

Vincent - That did it. Thanks. I can’t get my head around when I need to type cast and when I don’t!

Its tough I agree. As a rule of Thumb I do the following:

  • never cast anything or specify the type of anything unless VSCode indicates an error
  • if there is an error:
    • cast MyNumberItem.state Number, never DecimalType
    • cast Items to GenericItem where it is required (e.g. specifying the argument types of a lambda)

The Rule’s DSL is usually really good at managing type for you. The two exceptions are going from DateTimeType to Joda DateTime (the two are not in the same hierarchy so cannot be cast from one to the other) and Numbers. See https://docs.openhab.org/configuration/rules-dsl.html#datetime-item for DateTime conversions.

For Numbers I do the following:

  • Always cast the state from Number Items to Number rather than DecimalType. There is an ambiguous method call when you call sendCommand or postUpdate on a Number Item using DecimalType because there is a version of both methods that takes a DecimalType and another that takes a Number and since DecimalType is an instance of Number the Rules DSL doesn’t know which version of the method to call. ASIDE: This is probably at the level of a bug and the DecimalType method should probably be removed.

  • Sometimes you will want to use some of the methods out of the Math class (e.g. abs, round, etc). Those methods only accept primitives so you have to call .intValue or .floatValue: val abs = Math::abs((MyNumberItem.state as Number).floatValue)

Those are odd because .name and .lastUpdate are absolutely members of SwitchItem. I’ve even design patterns that require the ability to access the name of an Item in one of the collections lambdas (e.g. Associated Item val assoc = MyItemsGroup.members.findFirst[ a | a.name == MyItem.name+"_Assoc" ].

The code you have looks just fine to my eyes.

The errors go away when you take away the casting to SwitchItem?

1 Like

In this case you didn’t because the filter was always going to return an item. You need to type cast when the function return can be ambiguous.

Rich & Vincent,

VSCode… it wasn’t throwing an error… so I ought to have left well enough alone.

Yes, removing the explicit SwitchItem casting removes the error. I understand that in this situation there is no Type ambiguity so all is well if I don’t cast the variable. rhetorical - Why does it hurt to be explicit?

When I was trying different things to resolve what I now know is the unnecessary use of type casting, I had cast it as OnOffType one time. I got this error:

Could not cast `my_item` (Type=SwitchItem, State=OFF, Label=my item, Category=null, Groups=[`gAllItems`, `gMyItems`]) to org.eclipse.smarthome.core.library.types.OnOffType

I thought that this gave a clue in that it did not list name or lastUpdate in the list of attributes for a SwitchItem.

As mentioned already, removing the explicit SwitchItem casting allowed name & lastUpdate to be referenced just fine.

Conundrum :wink:

Regards.

Mike

I have no real good reason except to say that it works better for me not to. Xtend is supposed to be a weakly typed language and I encounter fewer errors when I code in it as such.

Every Item type carries two different types of state: NULL or whatever it’s normal state type is. NULL is a special state to indicate that the Item is uninitialized. NULL is not an instance of OnOffType so it cannot be cast to it.

I almost always try to filter those out in situations where I need to explicetly cast to a certain type (hey, there’s one reason to avoid unnecessary casting :slight_smile: ).

MyItem.members.filter[ i | i.state != NULL ].yadda yadda yadda

YAL - Yet Another Language… to need to master. Considering my first language was assembler to the 8086 when that CPU was the new kid (baby really) on the block, I suppose I ought not to complain about openHAB Xtend’s quirks. At least now I have the Internet where I can much more easily search for code examples. Oh, the memories of CompuServe, Bulletin Boards, 300 baud modems… yeah, the good 'ol days :wink:

I’m not much younger myself.

Prodigy and local BBS’s were my Internet before the Internet.

Anyway, if you want to work in a more standard language such as Python, JavaScript, or Groovy you can use the JSR223 add-on which lets you code in a more standard language. It isn’t quite as well documented in getting it set up and working but there are a few active users on this forum who can help. I personally like coding in the Rules DSL as I’ve reached the point where it makes sense. I wouldn’t choose it for doing anything else but applying the Design Patterns has allowed me to create some very powerful Rules in just a few lines of code that make sense, at least to me.