Visual battery voltage indicator

List and card widgets for displaying battery voltages and alternatively binary battery low-level switch items. Useful for maintenance overview pages.

Screenshots

Changelog

Version 1.0

  • initial release (voltage, lowlevel, voltage-repeater, lowlevel-repeater)

Usage

Generally straightforward.

  • The voltage list item expects a Number item formatted as a voltage, so a typed item
  • The low-level list item expects a Switch type, where OFF is “okay” and ON is “battery low”

You will need to adjust some formatting lines if you want the labels to look nice, specifically in the repeater widgets.

For example, I use:

label: =( ('' + loop.member.name) .replace(/^HMRT/, '') .replace(/_Battery$/,
                '') .replace(/([a-z])([A-Z])/g, '$1 $2')
                .replace(/([A-Za-z])([0-9])/g, '$1 $2') )

to reformat item names called eg. HMRTMasterBedroom1_Battery into Master Bedroom 1 because my item labels for the battery items are not very descriptive (they just say “Battery state”). If you have descriptive labels, maybe replace this (and similar) with a simple loop.member.label instead.

Known issues

  • The expressions that evaluate the opacity of the “cells” in the battery indicators is a bit messy, and I think there are still some peculiarities in there.

Resources

Version 1.0

YAML: Voltage listitem
uid: battery-voltage-listitem
tags:
  - battery
props:
  parameters:
    - label: Voltage item
      name: item
      required: true
      type: TEXT
      context: item
    - label: Left label text (optional)
      name: label
      required: false
      type: TEXT
    - label: Minimum voltage (default 2.5)
      name: minV
      required: false
      type: TEXT
    - label: Maximum voltage (default 3.0)
      name: maxV
      required: false
      type: TEXT
    - label: Green threshold (default 2.7)
      name: greenV
      required: false
      type: TEXT
    - label: Yellow threshold (default midpoint)
      name: yellowV
      required: false
      type: TEXT
timestamp: Dec 27, 2025, 9:35:41 PM
component: f7-list-item
config:
  title: =props.label || (items[props.item] && items[props.item].label) || props.item
  style:
    align-items: center
    padding-top: 6px
    padding-bottom: 6px
    --f7-list-item-title-white-space: nowrap
    --f7-list-item-title-overflow: hidden
    --f7-list-item-title-text-overflow: ellipsis
slots:
  after:
    - component: f7-block
      config:
        style:
          margin: 0
          padding: 0
          width: 100%
          height: 30px
          position: relative
          overflow: visible
          background: transparent
          background-color: transparent
      slots:
        default:
          - component: f7-block
            config:
              style:
                margin: 0
                padding: 0
                width: 100px
                height: 30px
                position: relative
                overflow: hidden
                border: 2px solid var(--f7-text-color)
                border-radius: 7px
                box-sizing: border-box
                background: transparent
                background-color: transparent
            slots:
              default:
                - component: f7-block
                  config:
                    style:
                      margin: 0
                      padding: 3px
                      width: 100%
                      height: 100%
                      display: flex
                      gap: 3px
                      box-sizing: border-box
                      align-items: stretch
                      justify-content: space-between
                      background: transparent
                      background-color: transparent
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 1) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 2) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 3) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 4) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 5) && 0.9 ) || 0.12 )
                - component: f7-block
                  config:
                    style:
                      position: absolute
                      left: 0
                      right: 0
                      top: 0
                      bottom: 0
                      display: flex
                      align-items: center
                      justify-content: center
                      margin: 0
                      padding: 0
                      font-weight: 700
                      font-size: 13px
                      color: var(--f7-text-color)
                      pointer-events: none
                      z-index: 2
                  slots:
                    default:
                      - component: Label
                        config:
                          text: =(''+((items[props.item] && (items[props.item].displayState ||
                            items[props.item].state)) || ''))
          - component: f7-block
            config:
              style:
                position: absolute
                left: 98px
                top: 8px
                width: 6px
                height: 14px
                border-radius: 4px
                border: 2px solid var(--f7-text-color)
                box-sizing: border-box
                background: white
                background-color: white
                margin: 0
                padding: 0

YAML: Low Level listitem
uid: battery-voltage-listitem
tags:
  - battery
props:
  parameters:
    - label: Voltage item
      name: item
      required: true
      type: TEXT
      context: item
    - label: Left label text (optional)
      name: label
      required: false
      type: TEXT
    - label: Minimum voltage (default 2.5)
      name: minV
      required: false
      type: TEXT
    - label: Maximum voltage (default 3.0)
      name: maxV
      required: false
      type: TEXT
    - label: Green threshold (default 2.7)
      name: greenV
      required: false
      type: TEXT
    - label: Yellow threshold (default midpoint)
      name: yellowV
      required: false
      type: TEXT
timestamp: Dec 27, 2025, 9:35:41 PM
component: f7-list-item
config:
  title: =props.label || (items[props.item] && items[props.item].label) || props.item
  style:
    align-items: center
    padding-top: 6px
    padding-bottom: 6px
    --f7-list-item-title-white-space: nowrap
    --f7-list-item-title-overflow: hidden
    --f7-list-item-title-text-overflow: ellipsis
slots:
  after:
    - component: f7-block
      config:
        style:
          margin: 0
          padding: 0
          width: 100%
          height: 30px
          position: relative
          overflow: visible
          background: transparent
          background-color: transparent
      slots:
        default:
          - component: f7-block
            config:
              style:
                margin: 0
                padding: 0
                width: 100px
                height: 30px
                position: relative
                overflow: hidden
                border: 2px solid var(--f7-text-color)
                border-radius: 7px
                box-sizing: border-box
                background: transparent
                background-color: transparent
            slots:
              default:
                - component: f7-block
                  config:
                    style:
                      margin: 0
                      padding: 3px
                      width: 100%
                      height: 100%
                      display: flex
                      gap: 3px
                      box-sizing: border-box
                      align-items: stretch
                      justify-content: space-between
                      background: transparent
                      background-color: transparent
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 1) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 2) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 3) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 4) && 0.9 ) || 0.12 )
                      - component: f7-block
                        config:
                          style:
                            margin: 0
                            padding: 0
                            flex: 1
                            height: 100%
                            min-width: 0
                            border-radius: 4px
                            background: =( ( (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) <= (Number(props.minV)||2.5)) &&
                              'var(--f7-color-red)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) < (Number(props.greenV)||2.7)) &&
                              'var(--f7-color-yellow)' ) || (
                              (Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) && 'var(--f7-color-green)' ) ||
                              'var(--f7-color-gray)' )
                            opacity: =( ( (Number((''+(items[props.item] && (items[props.item].displayState
                              ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0]) === Number((''+(items[props.item] &&
                              (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])) &&
                              (Math.round(Math.max(0,Math.min(1,(Number((''+(items[props.item]
                              && (items[props.item].displayState ||
                              items[props.item].state))).replace(',','.').split('
                              ')[0])-(Number(props.minV)||2.5))/((Number(props.maxV)||3.0)-(Number(props.minV)||2.5))))*5)
                              >= 5) && 0.9 ) || 0.12 )
                - component: f7-block
                  config:
                    style:
                      position: absolute
                      left: 0
                      right: 0
                      top: 0
                      bottom: 0
                      display: flex
                      align-items: center
                      justify-content: center
                      margin: 0
                      padding: 0
                      font-weight: 700
                      font-size: 13px
                      color: var(--f7-text-color)
                      pointer-events: none
                      z-index: 2
                  slots:
                    default:
                      - component: Label
                        config:
                          text: =(''+((items[props.item] && (items[props.item].displayState ||
                            items[props.item].state)) || ''))
          - component: f7-block
            config:
              style:
                position: absolute
                left: 98px
                top: 8px
                width: 6px
                height: 14px
                border-radius: 4px
                border: 2px solid var(--f7-text-color)
                box-sizing: border-box
                background: white
                background-color: white
                margin: 0
                padding: 0

YAML: Voltage repeater
uid: battery-voltage-group-repeater
tags:
  - battery
props:
  parameters:
    - context: item
      label: Group item name (required)
      name: groupItem
      required: true
      type: TEXT
    - context: Title
      label: Card Title text
      name: cardTitle
      required: false
      type: TEXT
    - label: Minimum voltage (default 2.5)
      name: minV
      required: false
      type: TEXT
    - label: Maximum voltage (default 3.0)
      name: maxV
      required: false
      type: TEXT
    - label: Green threshold (default 2.7)
      name: greenV
      required: false
      type: TEXT
    - label: Yellow threshold (default midpoint)
      name: yellowV
      required: false
      type: TEXT
timestamp: Dec 28, 2025, 10:18:42 AM
component: oh-list-card
config:
  title: =props.cardTitle
slots:
  default:
    - component: oh-repeater
      config:
        for: member
        sourceType: itemsInGroup
        groupItem: =props.groupItem
      slots:
        default:
          - component: widget:battery-voltage-listitem
            config:
              item: =loop.member.name
              label: =( ('' + loop.member.name) .replace(/^HMRT/, '') .replace(/_Battery$/,
                '') .replace(/([a-z])([A-Z])/g, '$1 $2')
                .replace(/([A-Za-z])([0-9])/g, '$1 $2') )
              minV: =props.minV
              maxV: =props.maxV
              greenV: =props.greenV
              yellowV: =props.yellowV

YAML: Low Level repeater
uid: battery-lowlevel-group-repeater
tags:
  - battery
props:
  parameters: 
    - context: item
      label: Group item name (required)
      name: groupItem
      required: true
      type: TEXT
    - label: Card Title
      name: cardTitle
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Dec 27, 2025, 9:52:00 PM
component: oh-list-card
config:
  title: =props.cardTitle
slots:
  default:
    - component: oh-repeater
      config:
        for: member
        sourceType: itemsInGroup
        groupItem: =props.groupItem
        filter: "!loop.member.name.includes('HMRT')"
      slots:
        default:
          - component: widget:battery-low-switch-listitem
            config:
              item: =loop.member.name
              label: "=( 'Window: ' +
                loop.member.name.replace(/^TFK/,'').replace(/_battery$/,'').rep\
                lace(/_(ONLY|HIGH|LOW)$/,' - $1').replace(/_/g,'
                ').replace(/([a-z])([A-Z])/g,'$1 $2').replace(/(\\D)(\\d+)/g,'$1
                $2').replace(/ - ONLY$/,' - Only').replace(/ - HIGH$/,' -
                High').replace(/ - LOW$/,' - Low') )"

2 Likes

Unfortunately, the marketplace currently only supports one widget per posting. So you’ll need to split this into four separate postings in order for users to be able to install them all.

Hi @Marmoset_Threat,

can you check your post - it seems that the code snippets for

  • YAML: Voltage listitem
  • YAML: Low Level listitem

both contain the code for ‘battery-voltage-listitem‘.

But the code for ‘battery-low-switch-listitem‘ used in `YAML: Low Level repeater` is missing.

Thanks