Fronius Gen24 Energy Widget

I have taken the SMA Widget " Animated Energy Widget" from Sebastian as basis for this Fronius (Gen24) widget that (tries to) replicate solarweb. It is not animated (yet) and the code quality/efficiency is basic, but after some weeks of testing, it seems to be doing what it should.

Main features:

  • Shows PV production and splits consumption into “consumed by house” & “consumed by Ohmpilot”
  • Shows water temperature
  • Shows power from/to grid
  • Shows battery state of charge and whether battery is charging or discarging
uid: energy_overview_widget_Fronius_2
tags: []
props:
  parameters:
    - context: item
      label: Netzeinspeisung
      name: netzeinspeisung
      required: true
      type: TEXT
    - context: item
      label: Netzbezug
      name: netzbezug
      required: true
      type: TEXT
    - context: item
      label: Gesamtverbrauch
      name: gesamtverbrauch
      required: true
      type: TEXT
    - context: item
      label: PV Leistung
      name: pv_leistung
      required: true
      type: TEXT
    - context: item
      label: Batterieleistung
      name: batterieleistung
      required: true
      type: TEXT
    - context: item
      label: Batterie Ladezustand
      name: batterylevel
      required: true
      type: TEXT
    - context: item
      label: Verbrauch Ohmpilot
      name: consumptionOhmpilot
      required: true
      type: TEXT
    - context: item
      label: Temperatur Ohmpilot
      name: temperatureOhmpilot
      required: true
      type: TEXT
    - context: item
      label: Self Consumption
      name: selfConsumption
      required: true
      type: TEXT
    - context: item
      label: Current Relative Autonomy
      name: relativeAutonomy
      required: true
      type: TEXT
    - context: item
      label: AC Voltage
      name: acVoltage
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Apr 24, 2024, 7:11:48 AM
component: f7-card
config:
  class:
    - display-flex
    - flex-direction-column
    - align-items-center
  style:
    height: 383px
  title: Energy Overview .......................................................................
slots:
  content:
    - component: f7-block
      config:
        style:
          --f7-theme-color: var(--f7-text-color)
          display: flex
          justify-content: space-between
          padding: 0
      slots:
        default:
          - component: f7-col
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
            slots:
              default:
                - component: f7-block
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                      height: 110px
                      justify-content: center
                      margin-top: 20px
                      width: 110px
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 2px solid gray
                            border-radius: 50px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: top
                            margin-right: 70px
                            margin-top: 160px
                            padding-top: 100px
                            width: 102px
                      - component: oh-icon
                        config:
                          height: 70px
                          icon: fronius_grid
                          style:
                            margin-right: 70px
                            margin-top: -80px
                            text-align: center
                            width: 45px
                      - component: Label
                        config:
                          style:
                            color: gray
                            font-size: 15px
                            margin-left: -60px
                            margin-top: 0px
                            text-align: center
                            width: 100px
                          text: = Number.parseInt(items[props.netzeinspeisung].state.toString().replace('-',' '))  + ' W'
                          textNew: = Number.parseInt(items[props.netzeinspeisung].state) + ' W'
                          textOld: =items[props.netzeinspeisung].state
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) < 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: orange
                            font-size: 15px
                            margin-left: -60px
                            margin-top: 0px
                            text-align: center
                            width: 100px
                          text: = Number.parseInt(items[props.netzeinspeisung].state.toString().replace('-',' '))  + ' W'
                          textNew: = Number.parseInt(items[props.netzeinspeisung].state) + ' W'
                          textddd: =items[props.netzeinspeisung].state
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) > 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: "#6cbe58"
                            font-size: 15px
                            margin-left: -60px
                            margin-top: 0px
                            text-align: center
                            width: 100px
                          text: =items[props.netzeinspeisung].state
                          textNew: = Number.parseInt(items[props.netzeinspeisung].state) + ' W'
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) == 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: gray
                            font-size: 9px
                            font-weight: bold
                            margin-right: 70px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = 'to grid'
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) < 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: orange
                            font-size: 9px
                            font-weight: bold
                            margin-right: 70px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = 'from grid'
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) > 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: "#6cbe58"
                            font-size: 9px
                            font-weight: bold
                            margin-right: 70px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = '----'
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) == 0) ? true : false'
          - component: f7-row
            config:
              preserveAspectRatio: xMidYMid slice
              style:
                height: auto
              tag: svg
              viewBox: 0 0 100 100
              xmlns: http://www.w3.org/2000/svg
            slots:
              default:
                - component: f7-row
                  config:
                    d: M53,0 v15 c0,40 10,35 30,35 h20
                    fill: none
                    id: path1
                    stroke: orange
                    stroke-width: 1
                    tag: path
                    vector-effect: non-scaling-stroke
                - component: f7-row
                  config:
                    fill: orange
                    r: 1
                    style:
                      stroke-width: 4
                    tag: circle
                    vector-effect: non-scaling-stroke
                  slots:
                    default:
                      - component: f7-row
                        config:
                          calcMode: linear
                          dur: 5s
                          repeatCount: indefinite
                          tag: animateMotion
                        slots:
                          default:
                            - component: f7-row
                              config:
                                tag: mpath
                                xlink:href: "#path1"
          - component: f7-col
            config:
              style:
                align-items: center
                display: flex
                flex-direction: column
                flex-grow: 1
            slots:
              default:
                - component: f7-block
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                      height: 110px
                      justify-content: center
                      margin-top: 0
                      width: 110px
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 2px solid orange
                            border-radius: 50px
                            borderOld: 2px solid teal
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: top
                            margin-right: 320px
                            margin-top: -50px
                            padding-top: 100px
                            width: 102px
                      - component: oh-icon
                        config:
                          height: 50px
                          heightNew: '=(Number.parseInt(items[props.pv_leistung].state.split(" ")[0]) > 100 && Number.parseFloat(items[props.pv_leistung].state.split(" ")[0]) < 1000) ? "50px" : "100px"'
                          icon: fronius_pv2
                          style:
                            margin-right: 320px
                            margin-top: -80px
                            text-align: center
                            width: 100px
                      - component: Label
                        config:
                          style:
                            font-size: 15px
                            margin-right: 320px
                            margin-top: -10px
                            text-align: center
                            width: 100px
                          text: = Number.parseInt(items[props.pv_leistung].state) + ' W'
                          textOld: =items[props.pv_leistung].displayState
                      - component: Label
                        config:
                          style:
                            color: gray
                            font-size: 9px
                            font-weight: bold
                            margin-right: 320px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = 'Power'
                          visible: '=(Number.parseFloat(items[props.pv_leistung].state.split(" ")[0]) > 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: orange
                            font-size: 9px
                            font-weight: bold
                            margin-right: 320px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = 'No power'
                          visible: '=(Number.parseFloat(items[props.pv_leistung].state.split(" ")[0]) <= 0) ? true : false'
                - component: f7-block
                  config:
                    style:
                      align-items: center
                      border: 2px solid teal
                      border-radius: 50px
                      display: flex
                      flex-direction: column
                      height: 100px
                      justify-content: top
                      margin-right: 40px
                      margin-top: -20px
                      padding-top: 0px
                      width: 102px
                  slots:
                    default:
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_inverter
                          style:
                            margin-right: 0px
                            margin-top: 15px
                            text-align: center
                            width: 100px
                      - component: Label
                        config:
                          style:
                            font-size: 11px
                            margin-top: -3px
                            text-align: center
                            width: 100px
                          text: =items[props.relativeAutonomy].displayState
                      - component: Label
                        config:
                          style:
                            font-size: 9px
                            margin-top: -3px
                            text-align: center
                            width: 100px
                          text: =items[props.acVoltage].displayState
                      - component: f7-row
                        config:
                          preserveAspectRatio: xMidYMid slice
                          style:
                            height: auto
                            width: auto
                          tag: svg
                          viewBox: 0 0 100 100
                          xmlns: http://www.w3.org/2000/svg
                        slots:
                          default:
                            - component: f7-row
                              config:
                                d: M60 -5 v10 c0 30 10 35 30 35 h20
                                fill: none
                                id: path1
                                stroke: rgba(100, 150, 200, 0.8)
                                stroke-width: 2
                                tag: path
                                vector-effect: non-scaling-stroke
                                visible: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) < 0) ? true : false'
                            - component: f7-row
                              config:
                                fill: rgba(100, 150, 200, 0.8)
                                r: 6
                                style:
                                  stroke-width: 4
                                tag: circle
                                vector-effect: non-scaling-stroke
                                visible: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) < 0) ? true : false'
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      calcMode: linear
                                      dur: 4s
                                      repeatCount: indefinite
                                      tag: animateMotion
                                    slots:
                                      default:
                                        - component: f7-row
                                          config:
                                            tag: mpath
                                            xlink:href: "#path1"
                            - component: f7-row
                              config:
                                d: M40 -5 v10 c0 40 -10 35 -30 35 h-20
                                fill: none
                                id: path2
                                stroke: rgba(100, 150, 200, 0.8)
                                stroke-width: 2
                                tag: path
                                vector-effect: non-scaling-stroke
                                visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) > 0 && Number.parseFloat(items[props.netzbezug].state.split(" ")[0]) == 0) ? true : false'
                            - component: f7-row
                              config:
                                fill: rgba(100, 150, 200, 0.8)
                                r: 6
                                strokeWidth: 10
                                tag: circle
                                vectorEffect: non-scaling-stroke
                                visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) > 0 && Number.parseFloat(items[props.netzbezug].state.split(" ")[0]) == 0) ? true : false'
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      calcMode: linear
                                      dur: 4s
                                      repeatCount: indefinite
                                      tag: animateMotion
                                    slots:
                                      default:
                                        - component: f7-row
                                          config:
                                            tag: mpath
                                            xlink:href: "#path2"
                            - component: f7-row
                              config:
                                d: M50, 0 v100
                                fill: none
                                id: path3
                                stroke: rgba(100, 150, 200, 0.8)
                                stroke-width: 1
                                tag: path
                                vector-effect: non-scaling-stroke
                                visible: =items[props.pv_leistung].displayState
                            - component: f7-row
                              config:
                                fill: rgba(100, 150, 200, 0.8)
                                r: 6
                                strokeWidth: 10
                                tag: circle
                                vectorEffect: non-scaling-stroke
                                visible: '=(Number.parseFloat(items[props.pv_leistung].state.split(" ")[0]) - (Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) + Math.abs(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]))) > 0) ? true : false'
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      calcMode: linear
                                      dur: 4s
                                      repeatCount: indefinite
                                      tag: animateMotion
                                    slots:
                                      default:
                                        - component: f7-row
                                          config:
                                            tag: mpath
                                            xlink:href: "#path3"
                - component: f7-row
                  config:
                    d: M 105 50 l -10 0 c -40 0 -35 10 -35 50 l 0 20
                    fill: none
                    id: path5
                    stroke: rgba(100, 150, 200, 0.8)
                    stroke-width: 2
                    tag: path
                    vector-effect: non-scaling-stroke
                    visible: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) > 0) ? true : false'
                - component: f7-row
                  config:
                    fill: rgba(100, 150, 200, 0.8)
                    r: 6
                    strokeWidth: 10
                    tag: circle
                    vectorEffect: non-scaling-stroke
                    visible: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) > 0) ? true : false'
                  slots:
                    default:
                      - component: f7-row
                        config:
                          calcMode: linear
                          dur: 4s
                          repeatCount: indefinite
                          tag: animateMotion
                        slots:
                          default:
                            - component: f7-row
                              config:
                                tag: mpath
                                xlink:href: "#path5"
                - component: f7-block
                  config:
                    style:
                      align-items: center
                      border: 2px solid teal
                      border-radius: 50px
                      display: flex
                      flex-direction: column
                      height: 100px
                      justify-content: top
                      margin-right: -240px
                      margin-top: -205px
                      padding-top: 0px
                      width: 102px
                  slots:
                    default:
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_consumption2
                          iconOLD: fronius_consumption
                          style:
                            margin-right: 0px
                            margin-top: 20px
                            text-align: center
                            width: 100px
                      - component: Label
                        config:
                          style:
                            font-size: 15px
                            margin-top: -10px
                            text-align: center
                            width: 100px
                          text: = Number.parseInt(items[props.gesamtverbrauch].state.toString().replace('-',' ')) - Number.parseInt(items[props.consumptionOhmpilot].state) + ' W'
                          textOld: =items[props.gesamtverbrauch].displayState
                          textOld2: = Number.parseInt(items[props.gesamtverbrauch].state) + ' W'
                          textOld3: = Number.parseInt(items[props.gesamtverbrauch].state.toString().replace('-',' ')) + ' W'
                      - component: oh-icon
                        config:
                          height: 30px
                          icon: ohmpilot_temperature
                          style:
                            margin-right: -150px
                            margin-top: -100px
                            text-align: center
                            width: 100px
                      - component: Label
                        config:
                          icon: ohmpilot_temperature
                          style:
                            font-size: 14px
                            margin-right: -225px
                            margin-top: -22px
                            text-align: center
                            width: 100px
                          text: =Number.parseInt(items[props.temperatureOhmpilot].state) + ' °C'
                          textOld: =Number.parseInt(items[props.temperatureOhmpilot].state) + (Number.parseInt('273')) + ' °C'
                      - component: Label
                        config:
                          style:
                            font-size: 14px
                            margin-right: -210px
                            margin-top: 0px
                            text-align: center
                            width: 100px
                          text: = Number.parseInt(items[props.consumptionOhmpilot].state) + ' W'
                          textOld: =items[props.consumptionOhmpilot].displayState
          - component: f7-col
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
            slots:
              default:
                - component: f7-block
                  config:
                    style:
                      align-items: center
                      border: 2px solid green
                      border-radius: 50px
                      display: flex
                      flex-direction: column
                      height: 100px
                      justify-content: top
                      margin-right: 50px
                      margin-top: 190px
                      padding-top: 0px
                      width: 102px
                  slots:
                    default:
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_battery_0
                          style:
                            margin-right: 0px
                            margin-top: 15px
                            text-align: center
                            width: 50px
                          text: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 0.01 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 0.25) ? true : false'
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_battery_charging_25
                          style:
                            margin-right: 0px
                            margin-top: 15px
                            text-align: center
                            width: 50px
                          text: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 0.25 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 0.50) ? true : false'
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_battery_charging_50
                          style:
                            margin-right: 0px
                            margin-top: 15px
                            text-align: center
                            width: 50px
                          text: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 0.50 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 0.75) ? true : false'
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_battery_charging_75
                          style:
                            margin-right: 0px
                            margin-top: 15px
                            text-align: center
                            width: 50px
                          text: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 0.75 && Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 0.90) ? true : false'
                      - component: oh-icon
                        config:
                          height: 50px
                          icon: fronius_battery_charging_100
                          style:
                            margin-right: 0px
                            margin-top: 15px
                            text-align: center
                            width: 50px
                          text: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) >= 0.90)  ? true : false'
                      - component: Label
                        config:
                          style:
                            color: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) > 0) ? "orange" : "#6cbe58"'
                            font-size: 15px
                            margin-top: -5px
                            text-align: center
                            white-space: nowrap
                            width: 100px
                          text: = Number.parseInt(items[props.batterieleistung].state.toString().replace('-',' ')) + ' W'
                          textNew: = Number.parseInt(items[props.batterieleistung].state) + ' W'
                          textOld: =items[props.batterieleistung].displayState
                      - component: Label
                        config:
                          style:
                            color: "#6cbe58"
                            font-size: 9px
                            font-weight: bold
                            margin-right: 0px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = 'charging'
                          visible: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) < 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: orange
                            font-size: 9px
                            font-weight: bold
                            margin-right: 0px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = 'depleting'
                          visible: '=(Number.parseFloat(items[props.batterieleistung].state.split(" ")[0]) > 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: gray
                            font-size: 11px
                            font-weight: bold
                            margin-right: 0px
                            margin-top: -5px
                            text-align: center
                            width: 100px
                          text: = '----'
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) == 0) ? true : false'
                      - component: oh-icon
                        config:
                          height: 30px
                          icon: fronius_battery
                          style:
                            margin-right: -150px
                            margin-top: -100px
                            text-align: center
                            width: 0px
                      - component: Label
                        config:
                          style:
                            color: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) < 0.30) ? "orange" : "#6cbe58"'
                            font-size: 12px
                            margin-right: -65px
                            margin-top: 15px
                            text-align: center
                            width: 100px
                          text: =Number.parseInt(items[props.batterylevel].displayState) + '%'
                          textNew: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) > 0.10) ? true : false'
                      - component: Label
                        config:
                          component: Label
                          style:
                            color: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) <= 0.10) ? "red" : "orange"'
                            font-size: 12px
                            margin-right: -68px
                            margin-top: 15px
                            text-align: center
                            width: 100px
                          text: =Number.parseInt(items[props.batterylevel].displayState) + '%'
                          textNew: =items[props.batterylevel].displayState
                          visible: '=(Number.parseFloat(items[props.batterylevel].state.split(" ")[0]) <= 0.10) ? true : false'

Here are the icons, add them to your openhab folder under conf/icons/classic :
fronius_battery_0
fronius_battery_75
fronius_battery_100_orange
fronius_battery_100
fronius_battery_charging_25
fronius_battery_charging_50
fronius_battery_charging_75
fronius_battery_charging_100_orange
fronius_battery_charging_100
fronius_battery
fronius_pv2
inverter_gen24
ohmpilot_temperature
fronius_consumption2
fronius_grid

2 Likes

I like the style of your widget. Currently I am working on something similar for my OH 4.2 installation.

I do have one question and maybe a suggestion to improve the readability of the code:

What is the meaning of the “textOldx” attributes on the Labels?

Now for my suggestion: In OH 4.2 you can use shortcuts (Widget Expressions & Variables | openHAB) to access the state of items:

'#item' for the numericalState
'@item' for the displayState
'@@item' for the state

They make the expressions much more concise and readable, removing the need to extract the numerical values from the displayState. The only downside to using them is you have to put the whole expressions in quotes so the editor is not confused by the #'s.