Help needed - writing my first custom Default List Item

Hi guys,

I just wrote my first custom list widget - but somehow I’m seemingly unable to arrange icon, label, stepper and chevron similar to the oh-list-item’s (icon & label arranged on the left side, stepper / chevron on the right side). Any hints?

My custom widget:

Default arrangement that I want to achieve:

Code

uid: radiator_list
tags:
  - list
  - radiator
props:
  parameters:
    - description: Widget Title
      label: Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: Radiator Item Group
      label: Group
      name: prefix
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jun 26, 2021, 3:49:43 PM
component: f7-list-item
slots:
  default:
    - component: f7-row
      slots:
        default:
          - component: f7-icon
            config:
              f7: thermometer
              color: '=(Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 23) ? "orange" : "red"'
          - component: Label
            config:
              text: =props.title
          - component: oh-stepper
            config:
              item: =props.prefix + "_SetTemperature"
              min: 15
              max: 23
              step: 0.5
              autorepeat: true
              autorepeatDynamic: true
          - component: oh-button
            config:
              iconF7: chevron_right
              action: sheet
              actionModal: widget:radiator_popup
              actionModalConfig:
                title: =props.title
                prefix: =props.prefix


I’m not using the default oh-stepper-item list widget since I’m using a stepper and a button ( > ) to open a widget with additional settings / info. You can add actions to the oh-stepper-item, but then every click on the stepper buttons also opens the popup…:

uid: radiator_popup
tags:
  - popup
  - radiator
props:
  parameters:
    - context: title
      description: Widget Title
      label: Title
      name: title
      required: false
      type: TEXT
    - context: prefix
      description: Radiator Item Group
      label: Group
      name: prefix
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jun 26, 2021, 3:46:20 PM
component: oh-list-card
config:
  title: =props.title
slots:
  default:
    - component: oh-list-item
      config:
        title: Aktuelle Temperatur
        icon: f7:thermometer
        iconColor: '=(Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 23) ? "orange" : "red"'
        after: =items[props.prefix + "_ActualTemperature"].state
        action: analyzer
    - component: oh-stepper-item
      config:
        title: Zieltemperatur
        icon: f7:thermometer
        iconColor: '=(Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 23) ? "orange" : "red"'
        item: =props.prefix + "_SetTemperature"
        min: 15
        max: 23
        step: 0.5
        autorepeat: true
        autorepeatDynamic: true
    - component: oh-list-item
      config:
        title: Boostmode
        icon: f7:flame
        iconColor: '=(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0) ? "orange" : "blue"'
        action: command
        actionItem: =props.prefix + "_BoostMode"
        actionCommand: ON
        badge: '=(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0) ? "Verlängern" : "Starten"'
        badgeColor: '=(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0) ? "red" : "green"'
        noChevron: true
    - component: oh-list-item
      config:
        title: Restdauer Boostmode
        icon: f7:clock
        after: =items[props.prefix + "_BoostState"].state
        visible: =(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0)
    - component: oh-list-item
      config:
        title: Signalstärke
        icon: f7:chart_bar
        iconColor: '=(items[props.prefix + "_SignalStrength"].state == "kein Signal") ? "red": "orange"'
        after: =items[props.prefix + "_SignalStrength"].displayState
        visible: =(Number.parseFloat(items[props.prefix + "_SignalStrength"].state) < 3)
    - component: oh-list-item
      config:
        title: Batterie schwach
        icon: f7:battery_25
        iconColor: red
        visible: =(items[props.prefix + "_LowBattery"].state == "ON")

I have to say our new MainUI is a nice additional rabbit hole to spend some weeks :flushed: :sweat_smile:

Edit:

Yes, this is possible. The trick is that in order for an list item to accordion the parent to that list item must have the accordionList: true setting. With the default list widgets you don’t actually have access to the list that is the parent of these items so you have to insert something in between. The oh-repeater is a perfect candidate for this because it allows you to give the list item a parent that doesn’t interfere with the rendering of the list item as part of a list. You just have to make sure that the settings you give the repeater only run once (so that you don’t duplicate your list item) and then you just ignore the repeaters loop variable in the actual list item widget.

Here’s the version I use for the default widgets for my window blinds. This renders a set of accordion buttons in based on the preset commands I have for the blinds.

uid: widget_blinds_default
tags:
  - default
  - blinds
props:
  parameters:
    - description: Label to show
      label: Item Label
      name: Label
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Apr 1, 2021, 8:46:30 PM
component: oh-repeater
config:
  for: dummyVar
  in:
    - nothing: here
  accordionList: true
slots:
  default:
    - component: oh-list-item
      config:
        icon: oh:blinds
        title: =props.Label
        after: =items[props.item].displayState
      slots:
        accordion:
          - component: oh-repeater
            config:
              sourceType: itemCommandOptions
              itemOptions: =props.item
              fragment: true
              for: option
            slots:
              default:
                - component: oh-button
                  config:
                    text: =loop.option.label
                    raised: true
                    action: command
                    actionItem: =props.item
                    actionCommand: =loop.option.command

blinds widget

2 Likes

Oh, this is great. After some confusion why only the first collapsed list item would be shown (seems like accordions will only hide / show one item) and working around that one by adding a list ‘in between’, it actually works:

Collapsed:

Expanded:

Code:

uid: radiator_list_v2
tags:
  - list
  - radiator
props:
  parameters:
    - description: Widget Title
      label: Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: Radiator Item Group
      label: Group
      name: prefix
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jun 28, 2021, 7:46:09 AM
component: oh-repeater
config:
  for: dummyVar
  in:
    - nothing: here
  accordionList: true
slots:
  default:
    - component: oh-stepper-item
      config:
        icon: f7:thermometer
        iconColor: '=(Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 23) ? "orange" : "red"'
        title: =props.title
        item: =props.prefix + "_SetTemperature"
        min: 15
        max: 23
        step: 0.5
        autorepeat: true
        autorepeatDynamic: true
        accordionItem: true
      slots:
        accordion:
          - component: oh-list
            config:
              mediaList: true
            slots:
              default:
                - component: oh-list-item
                  config:
                    title: Aktuelle Temperatur
                    icon: f7:thermometer
                    iconColor: '=(Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 23) ? "orange" : "red"'
                    after: =items[props.prefix + "_ActualTemperature"].state
                    action: analyzer
                - component: oh-stepper-item
                  config:
                    title: Zieltemperatur
                    icon: f7:thermometer
                    iconColor: '=(Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 23) ? "orange" : "red"'
                    item: =props.prefix + "_SetTemperature"
                    min: 15
                    max: 23
                    step: 0.5
                    autorepeat: true
                    autorepeatDynamic: true
                - component: oh-list-item
                  config:
                    title: Boostmode
                    icon: f7:flame
                    iconColor: '=(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0) ? "orange" : "blue"'
                    action: command
                    actionItem: =props.prefix + "_BoostMode"
                    actionCommand: ON
                    badge: '=(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0) ? "Verlängern" : "Starten"'
                    badgeColor: '=(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0) ? "red" : "green"'
                    noChevron: true
                - component: oh-list-item
                  config:
                    title: Restdauer Boostmode
                    icon: f7:clock
                    after: =items[props.prefix + "_BoostState"].state
                    visible: =(Number.parseFloat(items[props.prefix + "_BoostState"].state) > 0)
                - component: oh-list-item
                  config:
                    title: Signalstärke
                    icon: f7:chart_bar
                    iconColor: '=(items[props.prefix + "_SignalStrength"].state == "kein Signal") ? "red": "orange"'
                    after: =items[props.prefix + "_SignalStrength"].displayState
                    visible: =(Number.parseFloat(items[props.prefix + "_SignalStrength"].state) < 3)
                - component: oh-list-item
                  config:
                    title: Batterie schwach
                    icon: f7:battery_25
                    iconColor: red
                    visible: =(items[props.prefix + "_LowBattery"].state == "ON")
                - component: f7-list-item
                  config:
                    divider: true
                - component: oh-stepper-item
                  config:
                    title: Zieltemperatur bei Abwesenheit
                    subtitle: global - gilt für alle Räume
                    icon: f7:thermometer
                    iconColor: '=(Number.parseFloat(items.HeatingAbsence_SetTemperature.state) < 18.5) ? "green" : "red"'
                    item: HeatingAbsence_SetTemperature
                    min: 15
                    max: 23
                    step: 0.5
                    autorepeat: true
                    autorepeatDynamic: true
                - component: oh-stepper-item
                  config:
                    title: Zieltemperatur Nachts
                    subtitle: global - gilt für alle Räume
                    icon: f7:thermometer
                    iconColor: '=(Number.parseFloat(items.HeatingNight_SetTemperature.state) < 18.5) ? "green" : "red"'
                    item: HeatingNight_SetTemperature
                    min: 15
                    max: 23
                    step: 0.5
                    autorepeat: true
                    autorepeatDynamic: true

Now I just have to find out how to move the stepper item in line with the title (first pic / collapsed view) :sweat_smile:
Any ideas? :grin:

Odd. That appears to be a some bug with the stepper widget when the accordion chevron is drawn. You probably will have to make your base item a regular oh-list-item and then add a oh-stepper to the after slot of that item.

component: oh-repeater
config:
  for: dummyVar
  in:
    - nothing: here
  accordionList: true
slots:
  default:
    - component: oh-list-item
      config:
        icon: f7:thermometer
        iconColor: '=(Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_SetTemperature"].state) < 23) ? "orange" : "red"'
        title: =props.title
      slots:
        after:
          - component: oh-stepper
            config:
              item: =props.prefix + "_SetTemperature"
              min: 15
              max: 23
              step: 0.5
              autorepeat: true
              autorepeatDynamic: true
              style:
                padding-right: 10px
        accordion:
          - component: oh-list
            config:
              mediaList: true
            slots:
              default:
#etc...etc

That works :+1:

EDIT: Nope, thats not a good solution. It looks like it should - but every click on the stepper now also expands / collapses the accordion item :face_with_raised_eyebrow: Bug or feature? :thinking:

EDIT 2: And one additional problem - the first entry of the collapsable items, Aktuelle Temperatur should open the default analyzer widget - but it doesn’t. Any ideas why?

...
                - component: oh-list-item
                  config:
                    title: Aktuelle Temperatur
                    icon: f7:thermometer
                    iconColor: '=(Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 23) ? "orange" : "red"'
                    after: =items[props.prefix + "_ActualTemperature"].state
                    action: analyzer
...

Hmmm… that’s interesting. The expected behavior when a list item is an accordion is that clicking anywhere on the list item should trigger the expand/collapse. I had assumed that the extra widget would override that, not work in parallel. However, that appears to be the f7 behavior and not an OH bug. You may just not be able to accomplish both the stepper and the accordion in the same list item.

I don’t know if that’s the complete widget definition, but it looks like you are missing the actionAnalyzerItems key.

That would make sense - have to test it as soon as I’m back from work :sweat_smile:

Nope, neither actionAnalyzerItems or actionAnalyzerCoordSystem does fix the problem :thinking:

                - component: oh-list-item
                  config:
                    title: Aktuelle Temperatur
                    icon: f7:thermometer
                    iconColor: '=(Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 18) ? "blue" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 21) ? "green" : (Number.parseFloat(items[props.prefix + "_ActualTemperature"].state) < 23) ? "orange" : "red"'
                    after: =items[props.prefix + "_ActualTemperature"].state
                    action: analyzer
                    actionAnalyzerCoordSystem: time
                    actionAnalyzerItems: =props.prefix + "_ActualTemperature"

Have you tried:

actionAnalyzerItems: =items[props.prefix + "_ActualTemperature"]

Same result - nothing happens when I click on the list item.

In the working example I have the syntax looks like:

  action: analyzer
  actionAnalyzerItems:
    -  Partition3_Armed

Battling with the expression though :frowning:

1 Like

actionAnalyzerItems requires an array. There are two ways to do that here:

actionAnalyzerItems:
  - element 1
  - element 2
  - element 3

But as far as I know you won’t be able to use expression to dynamically generate item names that way. The other option is a single line array using brackets in an expression:

actionAnalyzerItems: =[element 1, element 2, element 3]

Because this is already an expression you can build whatever you need for each element, so in this case you want something like:

actionAnalyzerItems: =[props.prefix + "_ActualTemperature"]
1 Like

That does the trick :+1:

I know that feeling :sweat_smile:


So for now there is only one open problem :smiley: How to bring accordionList and oh-stepper together in a way that:

  • everything is in the same row like this
    image
  • but without interference between the stepper buttons and the expand/collapse action?

So my new idea is to discard the accordion list and to just set a switch to ON / OFF to decide if the additional settings / infos should be visible - see this topic.