A couple of simple oh-repeater examples

Hi Rich. I was using your code without any modification. I was able to answer my own question, however, just by poking around. “Member Base Type” in the Item definition has to be set to “Switch”. All my lights have this as “none” by default, so that was the problem. Thanks again for your code example.

Hey

Found you a way to order the oh-repeater results?

I’ve never looked. I don’t know if there is a way. I’d guess not.

That’s a shame, but thank you

Hi all,

these oh-repeaters make live much easier. I’m wondering, if there can be also some kind of dependencies to other items can be created.

Example:
1.) I have a list of items, containing temperatures for each room. All items tagged with ‘temperature’
2.) For each of the temperature items, I have a delta item, containing the delta temperature compared to the temperature 24 h ago. Value is either positive or negative.

Would it be possible to use oh-repeater to create a list of temperatures with a red icon color if temperature is going up (compared to 24 h ago) or green icon color if temperature is going down (according to relevant delta item value)? With manual assignment of the items, that is possible, but what about the oh-repeater?

Matthias

There are several different ways to achieve this. The easiest is just to make sure that your naming scheme is consistent. For every temperature item that has some name: temp_item_name then the corresponding delta item could be something like temp_item_name_delta. Then it is very simple to get the state of the delta item in the repeater from the temperature item with an expression like:

items[loop.repeaterVariable.name + "_delta"].state

A more complicated method, if your naming scheme is incompatible with such a usage, might be to use metadata for the temperature item to store the name of the delta item. Then you could utilize the fetchMetadata repeater property to collect that information along with the temperature item.

1 Like

According to @ysc:
When you’re iterating over items, then the widgetOrder metadata is considered:

fetchMetadata: semantics,widgetOrder

… should sort the results accordingly…

1 Like

I would like to create a widget that list all the items in a group with their default controls (i.e. a switch represented by a switch, or dimmer represented by a slider). This would look similar to the list that pops up when the Action is “Group” on an item, however I want this to be included directly on a UI page.

  • First, I don’t believe any of the standard widgets which support this, correct?
  • Second, if I use oh-repeater - how would I select the appropriate oh--item to display? Is there a “smart” oh--item which can be used to display the appropriate controls based on the item?

Thanks
Jeff

Hi all,
I fail to use numerical comparisons for Number:Dimensionless items in the oh-repeater filter.

The items I want to look at, represent the battery-charge of sensors and are set up as Number:Dimensionless.
From Jython, I can check for low battery using the floatValue():

[...]
        for item in battery_group.getMembers():

            if item.type == "Number:Dimensionless":
                # Battery modelled as fill-percentage
                if item.state.floatValue() < battery_low_warning_at_percent:
                    battery_low_warning.append(u"{} ({})".format(
                                                        battery_group.label,
                                                        item.state))
[...]

In my widget , I just cannot get hold of the numerical value for the item state.

title: =loop.item.label + " " + loop.item.type + " " + loop.item.state

prints out the state as e.g. 25 % and I can do string-machtes for that:

filter: items[loop.item.name].state == "25 %"

But how can I get to the numerical value and do, e.g.:

filter: items[loop.item.name].state <= 10

I tried things like loop.item.state.floatValue() (and other methods I knew of) but to no avail.
I was unable to find any documentation on what functions are supported by the filter-expression.
Is this documented somewhere? If not - wouldn’t that be a helpful addition?

In the widget editor the items object returns strings no matter what the item type because that’s how the information is returned from an api call. So if you want a numerical comparison, you have to convert the string using Number(). In your case you have to strip away the % but, because there’s a space, that’s easy with the split() method.

filter: Number(items[loop.item.name].state.split(" ")[0]) <= 10
2 Likes

@JustinG - thank you!
Is the filter expression evaluated by JavaScript in the end?
So I can use all the possibilities which are there in JS?

The expressions in widgets are not true javascript but jsep, a lightweight javascript-like expression parser:

A small comment (improvement?) - instead of using split, one can use

and with this it will work if you either have a number type or a type with a dimension attached to it … just FYI

2 Likes

parseInt and parseFloat are certainly useful alternatives. I can’t tell you if there’s a performance difference but if there is I suspect it’s minuscule. I prefer Number() in the widgets whenever possible because I like its null handling better for display (personal pet peeve seeing NaN’s in the UI), although as the filter expression is not a displayed value that’s less relevant in this case.

The split(" ") will also intelligently handle “76” vs “76 %” so no worries there.

As it might be interesting for someone else who’s looking for a widget, which will display battery warnings which are modelled as switches as well as battery levels which are modelled as percentages, here is my code.

uid: battery_warnings
tags:
  - battery
props:
  parameterGroups: 
    - description: "Configuration of widget behaviour"
      name: behaviour
    - description: "Widget setup"
      name: setup
  parameters:
    - description: "Only show batteries with warnings [on] or show all batteries [off]"
      groupName: behaviour
      label: Battery warnings only
      name: battWarnOnly
      required: true
      type: BOOLEAN
      default: true
    - description: "Percentage, below which batteries with a percentage indicator are considered as warning"
      groupName: behaviour
      label: Battery warning threshold
      name: battWarnThresh
      required: true
      type: INTEGER
      min: 0
      max: 100
      default: 15
    - description: "Label for battery warning items"
      groupName: setup
      label: Label
      name: itemlabel
      required: true
      type: TEXT
timestamp: Jul 28, 2021, 4:31:08 PM
component: f7-card
config:
  title: Batteriewarnungen
slots:
  default:
    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              fragment: true
              for: item
              sourceType: itemsWithTags
              itemTags: =props.itemlabel
              filter: items[loop.item.name].state == "ON" || Number.parseInt(items[loop.item.name]) < 15 || ! props.battWarnOnly
            slots:
              default:
                - component: oh-list-item
                  config:
                    icon: '=loop.item.state == "ON" || Number.parseInt(items[loop.item.name].state) < 15 ? "f7:battery_25" : "f7:battery_100"'
                    iconColor: '=loop.item.state == "ON" || Number.parseInt(items[loop.item.name].state) < 15 ? "red" : "green"'
                    title: '=loop.item.type.startsWith("Number") ? loop.item.label + " " + loop.item.state : loop.item.label'
                    item: =loop.item.name

PS: let me know if you spot something, that could be improved - I am always interested to learn more.

3 Likes

Can someone give a hint how to loop through a json Object in the oh-repeater.
I would like to loop show the keys and values items below the repeater.

The item (string) value is like this: {"C812105B04000400":"-93","blt.3.17q3si5345k00":"-48","blt.4.10heul64og400":"-68"}

I’m trying to make something like:
image

I can make something if I completely transform the json, but would like to archieve this without the transformation (or do the transformation in the widget)

uid: widget_bt_tab
tags: []
props:
  parameters:
    - description: Header
      label: log
      name: logArray
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: BT Item list
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Dec 4, 2021, 11:50:08 AM
component: f7-card
config:
  title: '="BT devices state: (" + items[props.item].state + ") " '
slots:
  default:
    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              for: btitems
              sourceType: array
              fragment: true
              in: '=JSON.parse("[ {\"device\": \"C812105B04000400\",\"strength\":\"-89\"},{\"device\":\"blt.3.17q3si5345k00\",\"strength\":\"-43\"},{\"device\":\"blt.4.10heul64og400\",\"strength\":\"-67\"}]")'
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: ...
                    badge: '=loop.btitems.strength'
                    badgeColor: '=(loop.btitems.strength  > -50) ? "green" : (loop.btitems.strength  <-80) ? "red" :  "yellow"'
                    icon: f7:wifi
                    style:
                      --f7-list-item-after-font-weight: bold
                    title: '= "Device " + loop.btitems.device '

To the best of my knowledge, the expression parser the widgets use is missing a few key pieces to do this easily by staying with the JSON (regex in string.replace(), and array.keys() method off the top of my head). I’d just resort to some simple string manipulation. For the repeater’s array strip the outer braces and use a simple string split:

in: =items.Test_JSONString.state.slice(1,-1).split(",")

which would make each of your loop variables "device name":"strength"

Then use a similar iteration of that to extract either the 0th element or the 1st element and strip the ". So the badge text would be:

badge: =loop.btitems.split(":")[1].slice(1,-1)

and the badge color would be the same but cast to a number for numerical comparisons:

badgeColor: '=(Number(loop.btitems.split(":")[1].slice(1,-1)) > -50) ? "green" : (Number(loop.btitems.split(":")[1].slice(1,-1)) < -80) ? "red" :  "yellow"'

and the device name just using the 0th element:

title: = "Device " + loop.btitems.split(":")[0].slice(1,-1)
1 Like

Great works like a charm!

Hi Jeff, did you find a solution?

I really like the OH3 UI and the standard UI rendering from the semantic model is really great. Especially with the possibility to assign custom widgets to the model items. Works fantastic for the standard page with tabs.

But all that just stops when developing pages and widgets. Is there a way to use the features from the semantic model? Something like.

    - component: oh-list
      slots:
        default:
          - component: oh-repeater
            config:
              for: item
              sourceType: itemsWithTags
              itemTags: Energy
              fragment: true
            slots:
              default:
                - component: default-list-item

Yes, that construction works for getting all items with the various semantic tags. You can even go one step further and add

fetchMetadata: semantics

to the repeater which will give you access to a little more model information including the name of the equipment item a point is a member of

loop.item.metadata.semantics.config.isPointOf

or the name of the location the equipment item is in

loop.item.metadata.semantics.config.hasLocation
1 Like