Sun2000 Widget with animation and popup

Sun2000_ud_v1

Ever since I saw widgets with animations here in the forum, I wanted to create a corresponding animation for my Huawei Sun2000 PV system. I learnt a lot about OpenHAB in the process.

My widget is based on the Deye Inverter widget from @kretzp and the tips from @JustinG in the community posts Popup for Shutter-Widget and f7-icon with custom icons.

The widget displays the most important places in the grid as coloured rectangles:

  • PV modules
  • storage
  • inverter
  • Power grid
  • House as consumer

The rectangles indicate the power that the respective component draws or supplies. The direction is also indicated by a coloured arrow.

  • Green: Power is supplied
  • Red: Power is drawn

The icons and texts in the rectangles serve as links; clicking on them opens a pop-up with further values.

Screenshots

Sun2000_ud_v2

Changelog

Version 0.1

  • initial release

Version 0.2

  • redesign of SVG part
  • long lines broken with multiline
  • changed popup linking from class to id

Resources

uid: Sun2000_Widget
tags:
  - Für Huawei Sun2000 Wechselrichter gem. https://www.debacher.de/wiki/Huawei_Sun2000_mit_OpenHAB
  - Version 0.2
props:
  parameters:
    - description: Widget Titel
      label: Widget Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: String1 Spannung (32016)
      label: PV1 Voltage
      name: item32016
      required: false
      type: TEXT
    - context: item
      description: String1 Strom (32017)
      label: PV1 current
      name: item32017
      required: false
      type: TEXT
    - context: item
      description: String2 Spannung (32018)
      label: PV2 Voltage
      name: item32018
      required: false
      type: TEXT
    - context: item
      description: String2 Strom (32019)
      label: PV2 current
      name: item32019
      required: false
      type: TEXT
    - context: item
      description: Solar Ertrag (32064)
      label: Solar Production
      name: item32064
      required: false
      type: TEXT
    - context: item
      description: Maximaler Ertrag (32078)
      label: Peak active today
      name: item32078
      required: false
      type: TEXT
    - context: item
      description: Leistung der Module (32080)
      label: Active Power
      name: item32080
      required: false
      type: TEXT
    - context: item
      description: Netz-Frequenz (32085)
      label: Grid frequency
      name: item32085
      required: false
      type: TEXT
    - context: item
      description: Inverter Temperatur (32087)
      label: Inverter Temperature
      name: item32087
      required: false
      type: TEXT
    - context: item
      description: Erzeugte Energie (32106)
      label: Acc. energy yield
      name: item32106
      required: false
      type: TEXT
    - context: item
      description: Heutiger Solar Ertrag (32114)
      label: Solar Daily Production
      name: item32114
      required: false
      type: TEXT
    - context: item
      description: Batterie Temperatur (37022)
      label: Battery Temperature
      name: item37022
      required: false
      type: TEXT
    - context: item
      description: Einspeisung/Bezug vom Netz (37113)
      label: Active power
      name: item37113
      required: false
      type: TEXT
    - context: item
      description: Gesamt-Einspeisung Netz (37119)
      label: Positive active electricity
      name: item37119
      required: false
      type: TEXT
    - context: item
      description: Gesamt-Bezug Netz (37121)
      label: Reverse active power
      name: item37121
      required: false
      type: TEXT
    - context: item
      description: Batterie Ladung (37760)
      label: Battery SOC
      name: item37760
      required: false
      type: TEXT
    - context: item
      description: Verbrauch/Einspeisung Batterie (37765)
      label: Charge/ Discharge power
      name: item37765
      required: false
      type: TEXT
    - context: item
      description: Heutige Einspeisung Batterie (37784)
      label: Daily Battery Charge
      name: item37784
      required: false
      type: TEXT
    - context: item
      description: Heutiger Verbrauch von Batterie (37786)
      label: Daily Battery Consumption
      name: item37786
      required: false
      type: TEXT
    - context: item
      description: Heutiger Netz-Bezug
      label: Daily Consumption From Grid
      name: itemDailyConsumptionFromGrid
      required: false
      type: TEXT
    - context: item
      description: Heutige Netz-Einspeisung
      label: Daily Production To Grid
      name: itemDailyProductionToGrid
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Mar 21, 2024, 9:57:43 AM
component: f7-card
config:
  title: = props.title
slots:
  content:
    - component: f7-block
      config:
        style:
          margin: 0
          padding: 0
      slots:
        default:
          - component: f7-block
            config:
              style:
                --f7-block-font-size: 12px
                --f7-theme-color: var(--f7-text-color)
                display: flex
                justify-content: center
                margin: 0
                padding: 0
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid orange
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-solar"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: orange
                                      height: 50px
                                      icon: if:game-icons:solar-power
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: green
                                iconF7: arrow_right
                                iconSize: 12px
                                popoverOpen: ="#info-solar"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: =items[props.item32064].state+" kW"
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            flex-direction: column
                            height: 70px
                            justify-content: center
                            width: 100px
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid teal
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  white-space: nowrap
                                text: =items[props.item37760].displayState+" %"
                            - component: oh-link
                              config:
                                action: popover
                                align-item: center
                                popoverOpen: ="#info-batt"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      height: 40px
                                      icon: battery
                                      state: =items[props.item37760].displayState
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: "=(Number.parseFloat(items[props.item37765].state) < 0) ? 'red' : 'green'"
                                iconF7: "=(Number.parseFloat(items[props.item37765].state) < 0) ? 'arrow_right' : 'arrow_left'"
                                iconSize: 12px
                                popoverOpen: ="#info-batt"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: = items[props.item37765].state+" kW"
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: svg
                        config:
                          style:
                            width: 20px
                            height: 270px
                          xmlns: http://www.w3.org/2000/svg
                        slots:
                          default:
                            - component: path
                              config:
                                d: M0,65 h2 q8,0 8,35 t8,35 h2
                                fill: none
                                id: solarpath
                                stroke: orange
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: orange
                                r: 4
                                style:
                                  stroke-width: 4
                                visible: "=(Math.abs(Number.parseFloat(items[props.item32064].state)) < 0.01) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Number.parseFloat(items[props.item32064].state) > 8) ? '1s' :
                                         (Number.parseFloat(items[props.item32064].state) > 4) ? '2s' :
                                         (Number.parseFloat(items[props.item32064].state) > 2) ? '3s' :
                                         (Number.parseFloat(items[props.item32064].state) > 1) ? '4s' :
                                         (Number.parseFloat(items[props.item32064].state) > 0.5) ? '6s' :
                                         (Number.parseFloat(items[props.item32064].state) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item32064].state) < 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#solarpath"
                            - component: path
                              config:
                                d: M0,230 h2 q8,0 8,-35 t8,-35 h2
                                fill: none
                                id: batterypath
                                stroke: teal
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: teal
                                r: 4
                                style:
                                  stroke-width: 4
                                visible: "=(Number.parseFloat(items[props.item37765].state) == 0) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Math.abs(Number.parseFloat(items[props.item37765].state)) > 8) ? '1s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 4) ? '2s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 2) ? '3s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 1) ? '4s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 0.5) ? '6s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item37765].state) > 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#batterypath"
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            flex-direction: column
                            height: 82px
                            justify-content: center
                            width: 100px
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 2px solid green
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-inverter"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: lightgreen
                                      height: 50px
                                      icon: if:cbi:huawei-solar-inverter
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: green
                                iconF7: arrow_right
                                iconSize: 12px
                                popoverOpen: ="#info-inverter"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: =items[props.item32080].state+" kW"
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: svg
                        config:
                          preserveAspectRatio: xMidYMid slice
                          style:
                            width: 20px
                            height: 270px
                          xmlns: http://www.w3.org/2000/svg
                        slots:
                          default:
                            - component: path
                              config:
                                d: M20,65 h-2 q-8,0 -8,35 t-8,35 h-2
                                fill: none
                                id: gridpath
                                stroke: darkred
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: darkred
                                r: 4
                                style:
                                  stroke-width: 4
                                tag: circle
                                visible: "=(Math.abs(Number.parseFloat(items[props.item37113].state)) < 0.01) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Math.abs(Number.parseFloat(items[props.item37113].state)) > 8) ? '1s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 4) ? '2s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 2) ? '3s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 1) ? '4s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 0.5) ? '6s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item37113].state) > 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#gridpath"
                            - component: path
                              config:
                                d: M20,230 h-2 q-8,0 -8,-35 t-8,-35 h-2
                                fill: none
                                id: loadpath
                                stroke: blue
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: blue
                                r: 4
                                style:
                                  stroke-width: 4
                                tag: circle
                                visible: "=(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) <= 0.01) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 8) ? '1s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 4) ? '2s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 2) ? '3s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 1) ? '4s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0.5) ? '6s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state) > 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#loadpath"
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid darkred
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-grid"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: darkred
                                      height: 50px
                                      icon: if:mdi:transmission-tower
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: "=(Number.parseFloat(items[props.item37113].state) < 0) ? 'red' : 'green'"
                                iconF7: "=(Number.parseFloat(items[props.item37113].state) > 0) ? 'arrow_right' : 'arrow_left'"
                                iconSize: 12px
                                popoverOpen: ="#info-grid"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: = Number.parseFloat(items[props.item37113].state).toFixed(3)+" kW"
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            flex-direction: column
                            height: 70px
                            justify-content: center
                            width: 100px
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid blue
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-home"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: blue
                                      height: 50px
                                      icon: if:healthicons:home-outline
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0) ? 'red' : 'green'"
                                iconF7: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0) ? 'arrow_right' : 'arrow_left'"
                                iconSize: 12px
                                popoverOpen: ="#info-home"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: >
                                  =(Number.parseFloat(items[props.item32080].state)
                                   - Number.parseFloat(items[props.item37113].state)).toFixed(3) + ' kW'
    - component: f7-popover
      config:
        id: ="info-solar"
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        style:
          background-color: snow2
          color: rgb(238,118,0)
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: >
                      =(Number.parseFloat(items[props.item32114].state)  
                       + Number.parseFloat(items[props.item37784].state)  
                       - Number.parseFloat(items[props.item37786].state)).toFixed(3) + ' kWh'
                    title: Ertrag Module
                - component: oh-list-item
                  config:
                    after: =items[props.item32078].displayState
                    title: Maximal Leistung
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32016].state*items[props.item32017].state)/1000).toFixed(3) + ' kW'
                    title: Leistung String 1
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32018].state*items[props.item32019].state)/1000).toFixed(3) + ' kW'
                    title: Leistung String 2
    - component: f7-popover
      config:
        id: ="info-batt"
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        style:
          background-color: snow2
          color: teal
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-label-item
                  config:
                    item: =props.item37784
                    title: Geladen
                - component: oh-label-item
                  config:
                    item: =props.item37786
                    title: Entladen
                - component: oh-label-item
                  config:
                    item: =props.item37022
                    title: Temp. Batterie
    - component: f7-popover
      config:
        id: ="info-inverter"
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        style:
          background-color: snow2
          color: green
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32114].state)).toFixed(3) + ' kWh'
                    title: Ertrag Inverter
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32087].state)).toFixed(1) + ' °C'
                    title: Temperatur
                - component: oh-label-item
                  config:
                    item: =props.item32085
                    title: Frequenz
    - component: f7-popover
      config:
        id: ="info-grid"
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        style:
          background-color: snow2
          color: darkred
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-label-item
                  config:
                    item: =props.itemDailyProductionToGrid
                    title: Netzeinspeisung
                - component: oh-label-item
                  config:
                    item: =props.itemDailyConsumptionFromGrid
                    title: Netzbezug
    - component: f7-popover
      config:
        id: ="info-home"
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        style:
          background-color: snow2
          color: blue
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: >
                      = (Number.parseFloat(items[props.item32114].state) 
                        -Number.parseFloat(items[props.itemDailyProductionToGrid].state) 
                        +Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state) ).toFixed(2)+ ' kWh'
                    title: Haus gesamt
                - component: oh-list-item
                  config:
                    after: >
                      = (Number.parseFloat(items[props.item32114].state) 
                        -Number.parseFloat(items[props.itemDailyProductionToGrid].state) ).toFixed(2)+ ' kWh'
                    title: Haus von PV
                - component: oh-list-item
                  config:
                    after: >
                      =(100-(Number.parseFloat(items[props.itemDailyProductionToGrid].state) 
                        / (Number.parseFloat(items[props.item32114].state) 
                         - Number.parseFloat(items[props.item37786].state) 
                         + Number.parseFloat(items[props.item37784].state)) )*100).toFixed(2) + ' %'
                    title: Eigenverbrauch heute
                - component: oh-list-item
                  config:
                    after: >
                      = (100*(Number.parseFloat(items[props.item32114].state) 
                          -Number.parseFloat(items[props.itemDailyProductionToGrid].state)) 
                         /(Number.parseFloat(items[props.item32114].state) 
                          -Number.parseFloat(items[props.itemDailyProductionToGrid].state) 
                          +Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state))).toFixed(2)+ ' %'
                    title: Autarkie
9 Likes

The listing roughly consists of three sections (even if the line numbers do not appear here):

  • Lines 1 to 138: Interface definition, where the elements used in the widget are defined
  • Lines 139 to 560: The structure of the graphic with the boxes and the animation
  • From line 561: Definition of the five pop-ups, one for each box

Interface
For all data elements that originate directly from the inverter, I have made sure that the register number appears in the item identifier. This makes the assignment in a Sun2000 clear and helps with the selection in the configuration dialogue.

At the end of the interface list you will find:

  • Today’s grid reference
  • Today’s grid feed-in

Unfortunately, these daily values are not supplied by the inverter. I will read them later directly from the electricity meter using a Homematic device. At the moment, however, they are variables that I fill via Rules.

Graphic and Animation
The actual graphic then consists of 5 columns.

  • Columns 1, 3 and 5 contain blocks with data and graphics
  • Columns 2 and 4 contain paths and animations
  • Some f7 elements are used in the realisation.

The animations are basically SVG elements that are implemented accordingly by OpenHAB and f7.

An important element in each case is the line, the path, in this case the solar path:

d: M0.35 h2 q3.0 3.15 t3.15 h2
Specifically, the following is defined here (the comma is always between the components of a point)

  • MoveTo (0.35)
  • Line horizontel 2 points
  • Square Bézier curve to the point (3.15) with the control point (3.0)
  • Square Bézier curve as above, but mirrored at point (3.15)
  • Horizontal line 2 points

For small letters the values are relative, for large letters they are absolute. This type of curve description is easier to understand and customise than the one using cubic curves.

In the following lines, an animation is then defined, which is based on a small coloured circle and is called via

xlink:href: “#solarpath
to the path defined above.

The following is defined for the animation:

  • dur: the duration in seconds, here as a complex case distinction
  • keyPoints: “=(Number.parseFloat(items[props.item32064].state) < 0) ? ‘1;0’ : ‘0;1’” depending on the sign, the direction of movement is different
  • keyTimes: 0;1 a uniform movement from start to finish

Popups
My aim was not to overload the actual graphic with information, but still to provide additional information. This is what the popups are for. It was important to me that no other widget is required for the content of the popups, i.e. that these elements are housed in the same widget.

The oh-link element allows action elements

 - component: oh-link
   config:
      action: popover
      popoverOpen: ="#info-solar"
      iconColour: green
      iconF7: arrow_right
      iconSize: 12px

The action element is of type popover, a class is assigned to it in line 197. The correct element is then controlled via this class

    - component: f7-popover
      config:
        id: info-solar
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true

It is important that the class names of the popover elements are different.

Using text lines of type oh-link for a popup is relatively straightforward. It was much more complicated to find a solution for the large icon. A block of type oh-icon does not allow a popup and an element of type oh-link does not allow a free choice of icon. Following instructions from JustinG, I found a solution:

  - component: oh-link
    config:
      action: popover
      popoverOpen: ="#info-solar"
    slots:
      default:
        - component: oh-icon
          config:
            icon: if:game-icons:solar-power
            height: 50px
            colour: orange

The oh-link has a slot that allows an oh-icon. This can also be used to make the large icon clickable.

3 Likes

Hello
Would you mind sharing the modbus configuration things and also items configuration for your setup?

Thank you

No problem, you can find my complete documentation at:

I have filed a description of the Modbus registers under:

1 Like

very detailed explanation nicely done like a professional … Thank you

another question how do you get this values ?
daily consumption from grid
daily consumption to grid

Unfortunately, these daily values are not supplied by the inverter. I currently use variables based on the lifetime values from registers 37119 and 37121, which I fill via rules:

See the Interface section in: OpenHAB Animation zur Sun2000 – Debacher-Wiki

The first rule is called at midnight and sets the start value for consumption and feed-in to the values of the data running over the service life.

just to make life easier for others this is my progress and thank you for the information:

most is file based config plus the yaml for the original widget
things


Bridge modbus:tcp:sun200 [ host="192.168.105.199", port=502, id=1 ,timeBetweenTransactionsMillis=5000, connectMaxTries=3, reconnectAfterMillis=60000, rtuEncoded=false, connectTimeoutMillis=5000, timeBetweenReconnectMillis=1000, afterConnectionDelayMillis=2000, afterConnectionDelayMillis=2000 , enableDiscovery=false ]{



Bridge poller slave104holding [ start=32016, length=8, refresh=5000, type="holding" ]{
    
        Thing data Pv_1_Voltage                    [ readStart="32016", readValueType="int16",readTransform="JS(divide10.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Pv_1_Current                    [ readStart="32017", readValueType="int16",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Pv_2_Voltage                    [ readStart="32018", readValueType="int16",readTransform="JS(divide10.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Pv_2_Current                    [ readStart="32019", readValueType="int16",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ]


   }

Bridge poller slave105holding [ start=32064, length=31, refresh=5000, type="holding" ]{
    
        Thing data Input_power                    [ readStart="32064", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Active_peak_of_current_day     [ readStart="32078", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Active_power_house                   [ readStart="32080", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Frequency                      [ readStart="32085", readValueType="uint16",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ] 
        Thing data Cabinet_temperature            [ readStart="32087", readValueType="int16",readTransform="JS(divide10.js)",updateUnchangedValuesEveryMillis="5000000" ]

    }

Bridge poller slave106holding [ start=32106, length=2, refresh=15000,  maxTries=3, cacheMillis=50, type="holding" ]{
        Thing data E_total                       [ readStart="32106", readValueType="uint32",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ]
    }

Bridge poller slave107holding [ start=32114, length=2, refresh=15000,  maxTries=3, cacheMillis=50, type="holding" ]{             
        Thing data E_day                         [ readStart="32114", readValueType="uint32",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ]  
    }

Bridge poller slave108holding [ start=37022, length=2, refresh=5000, type="holding" ]{
Thing data battery_temperature            [ readStart="37022", readValueType="int16",readTransform="JS(divide10.js)",updateUnchangedValuesEveryMillis="5000000" ]
}

Bridge poller slave109holding [ start=37113, length=2, refresh=5000, type="holding" ]{
    
        Thing data Active_power_direction                   [ readStart="37113", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
}

Bridge poller slave110holding [ start=37119, length=2, refresh=15000, type="holding" ]{
    
        Thing data Active_power_positive                   [ readStart="37119", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
}

Bridge poller slave111holding [ start=37121, length=2, refresh=15000, type="holding" ]{
    
        Thing data Active_power_negative                   [ readStart="37121", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
}

Bridge poller slave112holding [ start=37760, length=2, refresh=5000, type="holding" ]{
    
        Thing data Battery_soc                   [ readStart="37760", readValueType="uint16",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
}

Bridge poller slave113holding [ start=37765, length=2, refresh=1000, type="holding" ]{
    
        Thing data Charge_discarge_power                   [ readStart="37765", readValueType="int32",readTransform="JS(divide1000.js)",updateUnchangedValuesEveryMillis="5000000" ]
}

Bridge poller slave114holding [ start=37784, length=6, refresh=15000,  maxTries=3, cacheMillis=50, type="holding" ]{
        Thing data Battery_charge                       [ readStart="37784", readValueType="uint32",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ]
        Thing data Battery_consumption                      [ readStart="37786", readValueType="uint32",readTransform="JS(divide100.js)",updateUnchangedValuesEveryMillis="5000000" ]
    }


}

items

Group   gSolar "Solar data" <energy> (gss_general) ["Sensor"]


Number:ElectricPotential  Pv_1_Voltage        "Pv 1 voltage"    <energy>                                         (gSolar)              { unit="V" ,channel="modbus:data:sun200:slave104holding:Pv_1_Voltage:number" }
Number:ElectricCurrent  Pv_1_Current        "Pv 1 current"     <energy>                                         (gSolar)              { unit="A" ,channel="modbus:data:sun200:slave104holding:Pv_1_Current:number" }
Number:ElectricPotential  Pv_2_Voltage        "Pv 2 voltage"    <energy>                                         (gSolar)              { unit="V" ,channel="modbus:data:sun200:slave104holding:Pv_2_Voltage:number" }
Number:ElectricCurrent  Pv_2_Current        "Pv 2 current"     <energy>                                         (gSolar)              { unit="A" ,channel="modbus:data:sun200:slave104holding:Pv_2_Current:number" }
Number:Power  Input_power       "	AC input power"      <energy>                       (gSolar)                                  { unit="kW", channel="modbus:data:sun200:slave105holding:Input_power:number" }
Number:Power  Active_peak_of_current_day   	"Peak power of the last few hours"      <energy>                       (gSolar)           {unit="kW" ,channel="modbus:data:sun200:slave105holding:Active_peak_of_current_day:number" }
Number:Power  Active_power_house         "Actieve power house"      <energy>                       (gSolar)               {unit="kW", channel="modbus:data:sun200:slave105holding:Active_power_house:number" }
Number:Frequency  Frequency   "Frequency"     <energy>                       (gSolar)                           { unit="Hz",channel="modbus:data:sun200:slave105holding:Frequency:number" }
Number:Temperature   Cabinet_temperature  "Temperatuur inverter"<temperature>                             (gSolar)               { unit="°C",channel="modbus:data:sun200:slave105holding:Cabinet_temperature:number" }
Number:Energy  E_total            "Total produced"    <line>                                      (gSolar)               {unit="kWh", channel="modbus:data:sun200:slave106holding:E_total:number" }
Number:Energy  E_day              "Daily produced" <line>                                      (gSolar)               { unit="kWh",channel="modbus:data:sun200:slave107holding:E_day:number" }
Number:Temperature   Battery_temperature  "Temperature battery"<temperature>                              (gSolar)               { unit="°C",channel="modbus:data:sun200:slave108holding:battery_temperature:number" }
Number:Power   Active_power_direction         "Active power direction"      <energy>                (gSolar)               { unit="kW", channel="modbus:data:sun200:slave109holding:Active_power_direction:number" }
Number:Energy  Active_power_positive              "Active power positive" <line>                                      (gSolar)               { unit="kWh",channel="modbus:data:sun200:slave110holding:Active_power_positive:number" }
Number:Energy  Active_power_negative              "Active power negative" <line>                                      (gSolar)               { unit="kWh",channel="modbus:data:sun200:slave111holding:Active_power_negative:number" }
Number:Dimensionless Battery_percentage "Battery percentage [%.2f %%]" <batterylevel> (gSolar)       { channel="modbus:data:sun200:slave112holding:Battery_soc:number" }
Number:Power  Charge_discarge_power       "Charge discarge power"      <energy>                       (gSolar)                                  { unit="kW", channel="modbus:data:sun200:slave113holding:Charge_discarge_power:number" }
Number:Energy  Battery_charge            "Battery charge"    <line>                                      (gSolar)               { unit="kWh",channel="modbus:data:sun200:slave114holding:Battery_charge:number" }
Number:Energy  Battery_consumption            "Battery consumption"    <line>                                      (gSolar)               { unit="kWh",channel="modbus:data:sun200:slave114holding:Battery_consumption:number" }
Number DailyGridProduction
Number DailyGridConsumption
Number DailyGridFeedBegin
Number DailyGridConsumptionBegin

rules
here i still need to figure it out a little bit

rule "Update Daily Grid Feed and Consumption Begin"
when
    Time cron "0 0 0 * * ?" // Runs every day at 12:00 AM
then
    // Get the value of Active_power_positive
    val double positivePower = Active_power_positive.state as Number

    // Send the value to DailyGridFeedBegin item
    DailyGridFeedBegin.sendCommand(positivePower)

    // Get the value of Active_power_negative
    val double negativePower = Active_power_negative.state as Number

    // Send the value to DailyGridConsumptionBegin item
    DailyGridConsumptionBegin.sendCommand(negativePower)
end

rule "Update Daily Grid Consumption"
when
    Item Active_power_negative changed // Trigger when Active_power_negative is updated
then
    // Get the value of Active_power_negative
    val activePowerNegativeState = Active_power_negative.state
    if (activePowerNegativeState instanceof Number) {
        val double negativePower = (activePowerNegativeState as Number).doubleValue()

        // Get the value of DailyGridConsumptionBegin
        val consumptionBeginState = DailyGridConsumptionBegin.state
        if (consumptionBeginState instanceof Number) {
            val double consumptionBegin = (consumptionBeginState as Number).doubleValue()

            // Subtract consumptionBegin from negativePower
            val double dailyConsumption = negativePower - consumptionBegin

            // Send the calculated value to DailyGridConsumption item
            DailyGridConsumption.sendCommand(dailyConsumption)
        } else {
            logError("Rules", "DailyGridConsumptionBegin state is not a number")
        }
    } else {
        logError("Rules", "Active_power_negative state is not a number")
    }
end



rule "Update Daily Grid Production"
when
    Item Active_power_positive changed // Trigger when Active_power_positive is updated
then
    // Get the value of Active_power_positive
    val activePowerPositiveState = Active_power_positive.state
    if (activePowerPositiveState instanceof Number) {
        val double positivePower = (activePowerPositiveState as Number).doubleValue()

        // Get the value of DailyGridFeedBegin
        val feedBeginState = DailyGridFeedBegin.state
        if (feedBeginState instanceof Number) {
            val double feedBegin = (feedBeginState as Number).doubleValue()

            // Subtract feedBegin from positivePower
            val double dailyProduction = positivePower - feedBegin

            // Send the calculated value to DailyGridProduction item
            DailyGridProduction.sendCommand(dailyProduction)
        } else {
            logError("Rules", "DailyGridFeedBegin state is not a number")
        }
    } else {
        logError("Rules", "Active_power_positive state is not a number")
    }
end



updated widget

uid: Sun2000_Widget
tags:
  - Huawei
  - Version 0.2
props:
  parameters:
    - description: Widget Titel
      label: Widget Title
      name: title
      required: false
      type: TEXT
    - context: item
      description: String1 Spannung (32016)
      label: PV1 Voltage
      name: item32016
      required: false
      type: TEXT
    - context: item
      description: String1 Strom (32017)
      label: PV1 current
      name: item32017
      required: false
      type: TEXT
    - context: item
      description: String2 Spannung (32018)
      label: PV2 Voltage
      name: item32018
      required: false
      type: TEXT
    - context: item
      description: String2 Strom (32019)
      label: PV2 current
      name: item32019
      required: false
      type: TEXT
    - context: item
      description: Solar Ertrag (32064)
      label: Solar Production
      name: item32064
      required: false
      type: TEXT
    - context: item
      description: Maximaler Ertrag (32078)
      label: Peak active today
      name: item32078
      required: false
      type: TEXT
    - context: item
      description: Leistung der Module (32080)
      label: Active Power
      name: item32080
      required: false
      type: TEXT
    - context: item
      description: Netz-Frequenz (32085)
      label: Grid frequency
      name: item32085
      required: false
      type: TEXT
    - context: item
      description: Inverter Temperatur (32087)
      label: Inverter Temperature
      name: item32087
      required: false
      type: TEXT
    - context: item
      description: Erzeugte Energie (32106)
      label: Acc. energy yield
      name: item32106
      required: false
      type: TEXT
    - context: item
      description: Heutiger Solar Ertrag (32114)
      label: Solar Daily Production
      name: item32114
      required: false
      type: TEXT
    - context: item
      description: Batterie Temperatur (37022)
      label: Battery Temperature
      name: item37022
      required: false
      type: TEXT
    - context: item
      description: Einspeisung/Bezug vom Netz (37113)
      label: Active power
      name: item37113
      required: false
      type: TEXT
    - context: item
      description: Gesamt-Einspeisung Netz (37119)
      label: Positive active electricity
      name: item37119
      required: false
      type: TEXT
    - context: item
      description: Gesamt-Bezug Netz (37121)
      label: Reverse active power
      name: item37121
      required: false
      type: TEXT
    - context: item
      description: Batterie Ladung (37760)
      label: Battery SOC
      name: item37760
      required: false
      type: TEXT
    - context: item
      description: Verbrauch/Einspeisung Batterie (37765)
      label: Charge/ Discharge power
      name: item37765
      required: false
      type: TEXT
    - context: item
      description: Heutige Einspeisung Batterie (37784)
      label: Daily Battery Charge
      name: item37784
      required: false
      type: TEXT
    - context: item
      description: Heutiger Verbrauch von Batterie (37786)
      label: Daily Battery Consumption
      name: item37786
      required: false
      type: TEXT
    - context: item
      description: Heutiger Netz-Bezug
      label: Daily Consumption From Grid
      name: itemDailyConsumptionFromGrid
      required: false
      type: TEXT
    - context: item
      description: Heutige Netz-Einspeisung
      label: Daily Production To Grid
      name: itemDailyProductionToGrid
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Apr 15, 2024, 11:05:33 AM
component: f7-card
config:
  title: = props.title
slots:
  content:
    - component: f7-block
      config:
        style:
          margin: 0
          padding: 0
      slots:
        default:
          - component: f7-block
            config:
              style:
                --f7-block-font-size: 12px
                --f7-theme-color: var(--f7-text-color)
                display: flex
                justify-content: center
                margin: 0
                padding: 0
            slots:
              default:
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid orange
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-solar"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: orange
                                      height: 50px
                                      icon: if:game-icons:solar-power
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: green
                                iconF7: arrow_right
                                iconSize: 12px
                                popoverOpen: ="#info-solar"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: =items[props.item32064].state
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            flex-direction: column
                            height: 70px
                            justify-content: center
                            width: 100px
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid teal
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: Label
                              config:
                                style:
                                  white-space: nowrap
                                text: =items[props.item37760].displayState
                            - component: oh-link
                              config:
                                action: popover
                                align-item: center
                                popoverOpen: ="#info-batt"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      height: 40px
                                      icon: battery
                                      state: =items[props.item37760].displayState
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: "=(Number.parseFloat(items[props.item37765].state) < 0) ? 'red' : 'green'"
                                iconF7: "=(Number.parseFloat(items[props.item37765].state) < 0) ? 'arrow_right' : 'arrow_left'"
                                iconSize: 12px
                                popoverOpen: ="#info-batt"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: = items[props.item37765].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: svg
                        config:
                          style:
                            height: 270px
                            width: 20px
                          xmlns: http://www.w3.org/2000/svg
                        slots:
                          default:
                            - component: path
                              config:
                                d: M0,65 h2 q8,0 8,35 t8,35 h2
                                fill: none
                                id: solarpath
                                stroke: orange
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: orange
                                r: 4
                                style:
                                  stroke-width: 4
                                visible: "=(Math.abs(Number.parseFloat(items[props.item32064].state)) < 0.01) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Number.parseFloat(items[props.item32064].state) > 8) ? '1s' :
                                         (Number.parseFloat(items[props.item32064].state) > 4) ? '2s' :
                                         (Number.parseFloat(items[props.item32064].state) > 2) ? '3s' :
                                         (Number.parseFloat(items[props.item32064].state) > 1) ? '4s' :
                                         (Number.parseFloat(items[props.item32064].state) > 0.5) ? '6s' :
                                         (Number.parseFloat(items[props.item32064].state) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item32064].state) < 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#solarpath"
                            - component: path
                              config:
                                d: M0,230 h2 q8,0 8,-35 t8,-35 h2
                                fill: none
                                id: batterypath
                                stroke: teal
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: teal
                                r: 4
                                style:
                                  stroke-width: 4
                                visible: "=(Number.parseFloat(items[props.item37765].state) == 0) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Math.abs(Number.parseFloat(items[props.item37765].state)) > 8) ? '1s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 4) ? '2s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 2) ? '3s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 1) ? '4s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 0.5) ? '6s' :
                                         (Math.abs(Number.parseFloat(items[props.item37765].state)) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item37765].state) > 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#batterypath"
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            flex-direction: column
                            height: 82px
                            justify-content: center
                            width: 100px
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 2px solid green
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-inverter"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: lightgreen
                                      height: 50px
                                      icon: if:cbi:huawei-solar-inverter
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: green
                                iconF7: arrow_right
                                iconSize: 12px
                                popoverOpen: ="#info-inverter"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: =items[props.item32080].state
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: svg
                        config:
                          preserveAspectRatio: xMidYMid slice
                          style:
                            height: 270px
                            width: 20px
                          xmlns: http://www.w3.org/2000/svg
                        slots:
                          default:
                            - component: path
                              config:
                                d: M20,65 h-2 q-8,0 -8,35 t-8,35 h-2
                                fill: none
                                id: gridpath
                                stroke: darkred
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: darkred
                                r: 4
                                style:
                                  stroke-width: 4
                                tag: circle
                                visible: "=(Math.abs(Number.parseFloat(items[props.item37113].state)) < 0.01) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Math.abs(Number.parseFloat(items[props.item37113].state)) > 8) ? '1s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 4) ? '2s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 2) ? '3s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 1) ? '4s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 0.5) ? '6s' :
                                         (Math.abs(Number.parseFloat(items[props.item37113].state)) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item37113].state) > 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#gridpath"
                            - component: path
                              config:
                                d: M20,230 h-2 q-8,0 -8,-35 t-8,-35 h-2
                                fill: none
                                id: loadpath
                                stroke: blue
                                stroke-width: 2
                            - component: circle
                              config:
                                fill: blue
                                r: 4
                                style:
                                  stroke-width: 4
                                tag: circle
                                visible: "=(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) <= 0.01) ? false : true"
                              slots:
                                default:
                                  - component: animateMotion
                                    config:
                                      dur: >-
                                        =(Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 8) ? '1s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 4) ? '2s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 2) ? '3s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 1) ? '4s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0.5) ? '6s' :
                                         (Math.abs(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0.1) ? '8s' :'16s'
                                      keyPoints: "=(Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state) > 0) ? '1;0' : '0;1'"
                                      keyTimes: 0;1
                                      repeatCount: indefinite
                                    slots:
                                      default:
                                        - component: mpath
                                          config:
                                            xlink:href: "#loadpath"
                - component: f7-col
                  config:
                    style:
                      align-items: center
                      display: flex
                      flex-direction: column
                  slots:
                    default:
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid darkred
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-grid"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: darkred
                                      height: 50px
                                      icon: if:mdi:transmission-tower
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: "=(Number.parseFloat(items[props.item37113].state) < 0) ? 'red' : 'green'"
                                iconF7: "=(Number.parseFloat(items[props.item37113].state) > 0) ? 'arrow_right' : 'arrow_left'"
                                iconSize: 12px
                                popoverOpen: ="#info-grid"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: = Number.parseFloat(items[props.item37113].state).toFixed(3)+" kW"
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            flex-direction: column
                            height: 70px
                            justify-content: center
                            width: 100px
                      - component: f7-block
                        config:
                          style:
                            align-items: center
                            border: 3px solid blue
                            border-radius: 20px
                            display: flex
                            flex-direction: column
                            height: 100px
                            justify-content: center
                            width: 100px
                        slots:
                          default:
                            - component: oh-link
                              config:
                                action: popover
                                popoverOpen: ="#info-home"
                              slots:
                                default:
                                  - component: oh-icon
                                    config:
                                      color: blue
                                      height: 50px
                                      icon: if:healthicons:home-outline
                            - component: oh-link
                              config:
                                action: popover
                                iconColor: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0) ? 'red' : 'green'"
                                iconF7: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(items[props.item37113].state)) > 0) ? 'arrow_right' : 'arrow_left'"
                                iconSize: 12px
                                popoverOpen: ="#info-home"
                                style:
                                  font-size: 14px
                                  white-space: nowrap
                                text: >
                                  =(Number.parseFloat(items[props.item32080].state)
                                   - Number.parseFloat(items[props.item37113].state)).toFixed(3) + ' kW'
    - component: f7-popover
      config:
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        id: ="info-solar"
        style:
          background-color: snow2
          color: rgb(238,118,0)
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: >
                      =(Number.parseFloat(items[props.item32114].state)  
                       + Number.parseFloat(items[props.item37784].state)  
                       - Number.parseFloat(items[props.item37786].state)).toFixed(3) + ' kWh'
                    title: Yield modules
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32078].state)).toFixed(3) + ' kW'
                    title: Maximum performance
                - component: oh-list-item
                  config:
                    after: >
                      =(Number.parseFloat(items[props.item32016].state) * Number.parseFloat(items[props.item32017].state)).toFixed(3) /1000 + ' kW'
                    title: Performance string 1
                - component: oh-list-item
                  config:
                    after: >
                      =(Number.parseFloat(items[props.item32018].state) * Number.parseFloat(items[props.item32019].state)).toFixed(3) /1000 + ' kW'
                    title: Performance string 2
    - component: f7-popover
      config:
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        id: ="info-batt"
        style:
          background-color: snow2
          color: teal
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item37784].state)).toFixed(3) + ' kWh'
                    title: Loading
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item37786].state)).toFixed(3) + ' kWh'
                    title: Unloading
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item37022].state)).toFixed(1) + ' °C'
                    title: Temp. Battery
    - component: f7-popover
      config:
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        id: ="info-inverter"
        style:
          background-color: snow2
          color: green
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32114].state)).toFixed(3) + ' kWh'
                    title: Yield inverter
                - component: oh-list-item
                  config:
                    after: =(Number.parseFloat(items[props.item32087].state)).toFixed(1) + ' °C'
                    title: Temperature
                - component: oh-label-item
                  config:
                    after: =(Number.parseFloat(items[props.item32085].state)).toFixed(1) + ' Hz'
                    title: Frequency
    - component: f7-popover
      config:
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        id: ="info-grid"
        style:
          background-color: snow2
          color: darkred
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-label-item
                  config:
                    after: =(Number.parseFloat(items[props.itemDailyProductionToGrid].state)).toFixed(3) + ' kW'
                    title: Grid feed
                - component: oh-label-item
                  config:
                    after: =(Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state)).toFixed(3) + ' kW'
                    title: Grid consumption
    - component: f7-popover
      config:
        closeByBackdropClick: true
        closeByOutsideClick: true
        closeOnEscape: true
        id: ="info-home"
        style:
          background-color: snow2
          color: blue
          height: auto
          width: 250px
      slots:
        default:
          - component: oh-list
            config:
              simpleList: false
            slots:
              default:
                - component: oh-list-item
                  config:
                    after: >
                      = (Number.parseFloat(items[props.item32114].state) 
                        - Number.parseFloat(items[props.itemDailyProductionToGrid].state) 
                        + Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state)).toFixed(2) + ' kWh'
                    title: House entire
                - component: oh-list-item
                  config:
                    after: >
                      = (Number.parseFloat(items[props.item32114].state) 
                        -Number.parseFloat(items[props.itemDailyProductionToGrid].state) ).toFixed(2)+ ' kWh'
                    title: House of PV
                - component: oh-list-item
                  config:
                    after: >
                      =(100-(Number.parseFloat(items[props.itemDailyProductionToGrid].state) 
                        / (Number.parseFloat(items[props.item32114].state) 
                         - Number.parseFloat(items[props.item37786].state) 
                         + Number.parseFloat(items[props.item37784].state)) )*100).toFixed(2) + ' %'
                    title: Own consumption today
                - component: oh-list-item
                  config:
                    after: >
                      = (100*(Number.parseFloat(items[props.item32114].state) 
                          -Number.parseFloat(items[props.itemDailyProductionToGrid].state)) 
                         /(Number.parseFloat(items[props.item32114].state) 
                          -Number.parseFloat(items[props.itemDailyProductionToGrid].state) 
                          +Number.parseFloat(items[props.itemDailyConsumptionFromGrid].state))).toFixed(2)+ ' %'
                    title: Self-sufficiency

Very nice and clean widget! One suggestion would be to not include the units like kW and % in the widget but let every item ensure the correct unit is included in the state. I have my items set up like this but the widget now shows the units twice now after every number.

From my point of view, it is not a good idea to make this change.

Let’s take 37113 Active Power as an example.
In my configuration, this is of type Number (without dimension)
With the type Number.Power, I have two disadvantages:

  • Number.Power shows the unit W and not kW
  • In some places I then have to convert the string back into a number

I have entered the pattern %.3f kW under Metadata in stateDescription.

In the meantime, I generally do without dimensions for the type number

If in doubt, you just have to remove the units in the widget.