Several item states in oh:list widget

Good morning
I tried to maximise the information in an oh:list widget to show the conditions of all zwave and zigbee devices (battery level, and last seen).
Every device has items like ‘AAnn_LifeCheck’ or ‘AAnn_BatterieLadung’ etc , where AA is like ‘FS’ (Fenster (window)sensor or ‘LED’ (then three letters) and nn counts up from 01 to 09 for every device.
So the group ‘gLifeCheck’ is the loop index. From every loop item I take the name (like ‘FS01’) and add the rest (like 'BatterieLadung’), to find the corresponding item.
For every point ( badge or color) I used the same code, which becomes rather long.
Is there a chance to use a variable like dashPosition := loop.item.name.search("
") and use this
here

Number.parseFloat(items[loop.item.name.substr(0,loop.item.name.search("_"))+"_Batterieladung"].state

Number.parseFloat(items[loop.item.name.substr(0,dashPosition)+"_Batterieladung"].state

my widget code looks like

timestamp: Jul 21, 2024, 11:04:36 AM
component: oh-list-card
config:
  title: ="Zustand der Zwave / Zigbee Geräte"
slots:
  default:
    - component: oh-repeater
      config:
        for: item
        fragment: true
        groupItem: =props.gLifeCheck
        sourceType: itemsInGroup
      slots:
        default:
          - component: oh-list-item
            config:
              icon: '=(Number.parseFloat(items[loop.item.name.substr(0,loop.item.name.search("_"))+"_BatterieLadung"].state)
                > props.green) ? "f7:battery_100" :
                (Number.parseFloat(items[loop.item.name.substr(0,loop.item.name.search("_"))+"_BatterieLadung"].state)
                > props.orange) ? "f7:battery_25" : "f7:battery_0"'
              iconColor: '=(Number.parseFloat(items[loop.item.name.substr(0,loop.item.name.search("_"))+"_BatterieLadung"].state)
                > props.green) ? "green" :
                (Number.parseFloat(items[loop.item.name.substr(0,loop.item.name.search("_"))+"_BatterieLadung"].state)
                > props.orange) ? "orange" : "red"'
              badge: =items[loop.item.name.substr(0,loop.item.name.search("_"))+"_BatterieLadung"].state
                + " --> " +loop.item.state
              badgeColor: '=(loop.item.state) == "aktiv" ? "green": "red"'
              title: =loop.item.name.substr(0,loop.item.name.search("_")) + ' --> gesehen  '+
                items[loop.item.name.substr(0,loop.item.name.search("_"))+'_LifeCheckTimestamp'].displayState

and that’s how it finaly looks like

You cannot do variable assignment in widget expressions. However, in 4.2 an oh-context component has been added:

And you can see examples of how to use its three features in the variables, constants, and custom functions section of the widget help:

This is exactly the use case for a context function. In this case, because you would be using a function and not a variable specific to each loop iteration, you would put the context as the parent of the repeater and define the function once and you could then call it in each of your list-item expressions. Here’s an abbreviated outline:

component: oh-list-card
  - component: oh-context
    config:
      functions:
        getBattState: =(x) => Number.parseFloat(items[x.substr(0,x.search("_"))+"_BatterieLadung"].state)
    slots:
      default:
        - component: oh-repeater
          - component: oh-list-item
            config:
              icon: '=(fn.getBattState(loop.item.name) > props.green) ? "f7:battery_100" :
              ...'
2 Likes

OH 4.3 Build 4197

Hello JustinG
There seems to be a problem within

  parameterGroups: []
timestamp: Jul 25, 2024, 11:27:21 AM
component: oh-list-card
  - component: oh-context
    config:
      functions:
        getBattState: =(x) => Number.parseFloat(items[x.substr(0,x.search("_"))+"_BatterieLadung"].state)
    slots:
      default:
        - component: oh-repeater
          config:
            for: item
            fragment: true
            groupItem: =props.gLifeCheck
            sourceType: itemsInGroup

getting this fault

Nested mappings are not allowed in compact mappings at line 40, column 12:

component: oh-list-card
           ^

thanks for help

You cannot just copy and paste the code that I provided, it is incomplete. I just typed it up quickly while replying, to demonstrate the structure of the widget. The error you are getting is because:

component: oh-list-card
  - component: oh-context

is an invalid widget definition and invalid yaml syntax. It should be:

component: oh-list-card
slots:
  default:
    - component: oh-context

Thank‘s for your help, it will keep all silly user like me happy for the next years, when they can search for detailed informations here, even it costs you a lot more work. Thanks‘s again.
Everything works pretty good, I only have problems to ask for fn results been not a number.

This is where the widget expression tester is very helpful. If you open up the developer side bar (press the ? button at the top or press shift + alt + d) and go to Tools and the third tool button you will get this:

Enter example widget expressions in the box and you will see immediate results displayed. So, build up each part of your function to see where the problem is. For example, you know the the items where the result goes badly, so start with the string name of that item and make sure that the search string gets correct results:

=('some_item_name').substr(0,('some_item_name').search("_"))

If that works as expected, expand to the next level of complexity:

=items[('some_item_name').substr(0,('some_item_name').search("_"))+"_BatterieLadung"]

(this will show you the whole returned item object, not just the state)

Continue like this until you find the point where the result is not what you expect and you can start to debug the problem from there.

I don’t have your items and your values, so I can’t really do this part for you. But, most likely, the problem is that the states that are being returned from those items are not in a format that Number.parseFloat can handle. If that’s the case, then you’ll have to figure out what state those items do have and a way to either get the state into a more consistent format or expand the complexity of your function to handle those different states.

Sorry, I didn‘t explain it in a correct way. If the fn function gets an item name, which does not exists, the page shows a „-„ , but in the log you get a warning

2024-07-28 22:28:15.749 [WARN ] [e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn’t exist: SD05_BatterieLadung

To avoid that warning, I like to check if the return value of the Funktion is a number. I tried to check for UNDEF but it doesn’t work

Well, the expression tester is your friend here too. Use it to see what the return value of an expression like your function is when an invalid name is entered. You are right, the items object in widget expressions always returns - when an item doesn’t exist or it’s state is UNDEF. But, the items object is not the final process in your function, Number.parseFloat is. So you’ll need use the expression tester to know what that returns when the input is. Notably the answer will never be UNDEF that is specific to OH item states, and not a javascript value.

2 Likes

Thank‘s again, will have to delay these tests for some weeks as there is no expression tester in OH on the ipad.

You can make an educated guess, even if you don’t have access to the expression tester. The screen shot you posted shows that in the case of the missing items, the concatenated string is NaN%-->passiv. Nothing about string concatenation should result in a NaN response so that is most likely the direct result of the function in that case. You can test for for that with Number.isNaN() function.

=Number.isNaN(fn.getBattState(loop.item.name)) ? 'Result is not a number' : 'Result is a number'

Thank‘s to VPN and yourself we did the job. I will post the widget later on as soon as I have access to a pc.

Is it possible to use more than one function ?

functions:
funct1: = (x) => ….
funct2: = (x) => …

Yes, you can define as many functions as you like in the context component.


d: zwave_zigbee_geraete
2
tags: []
3
props:
4
  parameters:
5
    - context: item
6
      description: Gruppe Life Check
7
      label: Life Check gruppen Item
8
      name: gLifeCheck
9
      required: true
10
      type: TEXT
11
      filterCriteria:
12
        - value: Group
13
          name: type
14
  parameterGroups: []
15
timestamp: Aug 4, 2024, 6:14:09 AM
16
component: oh-list-card
17
config:
18
  title: ="Zustand der Zwave / Zigbee Geräte"
19
slots:
20
  default:
21
    - component: oh-context
22
      config:
23
        functions:
24
          getBattState: =(x) =>
25
            Number.parseFloat(items[x.substr(0,x.search("_"))+"_BatterieLadung"].state)
26
          getLifeCheck: =(x) =>
27
            items[x.substr(0,x.search("_"))+"_LifeCheckTimestamp"].displayState
28
          getName: =(x) => x.substr(0,x.search("_"))
29
        variables:
30
          greenLevel: =60
31
          orangeLevel: =30
32
      slots:
33
        default:
34
          - component: oh-repeater
35
            config:
36
              for: item
37
              fragment: true
38
              groupItem: =props.gLifeCheck
39
              sourceType: itemsInGroup
40
            slots:
41
              default:
42
                - component: oh-list-item
43
                  config:
44
                    badge: '=Number.isNaN(fn.getBattState(loop.item.name))? "- --> "+
45
                      loop.item.state : (fn.getBattState(loop.item.name))+ "%-->
46
                      "+ loop.item.state'
47
                    badgeColor: '=(loop.item.state) == "aktiv" ? "green": "red"'
48
                    icon: '=Number.isNaN(fn.getBattState(loop.item.name))?  "oh:energy":
49
                      (fn.getBattState(loop.item.name) > vars.greenLevel)?
50
                      "f7:battery_100" : (fn.getBattState(loop.item.name) >
51
                      vars.orangeLevel) ? "f7:battery_25" : "f7:battery_0"'
52
                    iconColor: '=(fn.getBattState(loop.item.name) > vars.greenLevel) ? "green" :
53
                      (fn.getBattState(loop.item.name) > vars.orangeLevel) ?
54
                      "orange" : "red"'
55
                    title: =fn.getName(loop.item.name) + " --> gesehen  "+
56
                      fn.getLifeCheck(loop.item.name)

That‘s how it finally looks like

Back home I am still fighting the warning I get while using oh-contest

slots:
  default:
    - component: oh-context
      config:
        constants:
          greenLevel: 60
          orangeLevel: 30
        functions:
          getBattState: =(x) =>
            Number.parseFloat(items[x.substr(0,x.search("_"))+"_BatterieLadung"].state)

      slots:
        default:
          - component: oh-repeater
            config:
              for: item
              fragment: true
              groupItem: =props.gLifeCheck
              sourceType: itemsInGroup
            slots:
              default:
                - component: oh-list-item
                  config:
                    badge: '=Number.isNaN(fn.getBattState(loop.item.name))? "- --> "+
                      loop.item.state : (fn.getBattState(loop.item.name))+ "%-->
                      "+ loop.item.state'
2024-09-09 16:10:43.380 [WARN ] [e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: SD03_BatterieLadung
2024-09-09 16:10:43.381 [WARN ] [e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: SD04_BatterieLadung
2024-09-09 16:10:43.382 [WARN ] [e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: SD05_BatterieLadung



The warning tells me, that the item I am looking for does not exists, which is ok.
I think, the warning comes from the function itself and not from displaying the state of the non existing item. How can I avoid that message?

You are correct, and because it is just a warning there is no problem if you just choose to ignore it.

There are times when it can be tricky to avoid this message just because of the nature of the widget build (I myself still have one widget that produces these warnings on occasion as well). You need to have some way to get the information about whether the item exists without polling the item itself. Often this is more trouble than just ignoring the the warning. But, for example, you could add a metadata to each of the base items in your widget that specifies whether there is an associated BatterieLadung item, and only call the function if that metadata (which can be accessed via the repeater’s fetechMetadata property) returns true.

Alternatively, you could simply add the missing items, leave them at a NULL state and handle the null repsonse in the widget function.

Thanks, that‘s what I am going to do.