Animated Energy Widget

I have modified the original code slightly:

uid: SMA_widget
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
  parameterGroups: []
timestamp: Mar 22, 2022, 7:33:12 PM
component: f7-card
config:
  class:
    - display-flex
    - flex-direction-column
    - align-items-center
  style:
    height: 383px
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: 0
                      width: 110px
                  slots:
                    default:
                      - component: oh-icon
                        config:
                          height: 110px
                          icon: sma_grid_2
                      - component: Label
                        config:
                          style:
                            color: red
                            font-size: 20px
                            font-weight: bold
                            margin-top: -10px
                            text-align: center
                            width: 100px
                          text: =items[props.netzbezug].displayState
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) == 0) ? true : false'
                      - component: Label
                        config:
                          style:
                            color: green
                            font-size: 20px
                            font-weight: bold
                            margin-top: -10px
                            text-align: center
                            width: 100px
                          text: =items[props.netzeinspeisung].displayState
                          visible: '=(Number.parseFloat(items[props.netzeinspeisung].state.split(" ")[0]) > 0) ? true : false'
          - 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: Label
                        config:
                          style:
                            color: green
                            font-size: 20px
                            font-weight: bold
                            text-align: center
                            width: 100px
                          text: =items[props.pv_leistung].displayState
                      - component: oh-icon
                        config:
                          height: 110px
                          icon: sma_pv_2
                          style:
                            margin-top: -5px
                - component: f7-block
                  config:
                    style:
                      display: flex
                      justify-content: center
                      margin: 0
                      padding: 0
                      width: 100%
                  slots:
                    default:
                      - 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: 4
                                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(22, 160, 133, 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(22, 160, 133, 0.8)
                                r: 4
                                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(22, 160, 133, 0.8)
                                stroke-width: 2
                                tag: path
                                vector-effect: 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'
                            - component: f7-row
                              config:
                                fill: rgba(22, 160, 133, 0.8)
                                r: 4
                                strokeWidth: 2
                                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-5 50 l10 0 c40 0 35 10 35 50 l 0 20
                                fill: none
                                id: path4
                                stroke: rgba(255, 0, 0, 0.8)
                                stroke-width: 2
                                tag: path
                                vector-effect: non-scaling-stroke
                                visible: '=(Number.parseFloat(items[props.netzbezug].state.split(" ")[0]) > 0) ? true : false'
                            - component: f7-row
                              config:
                                fill: rgba(255, 0, 0, 0.8)
                                r: 4
                                strokeWidth: 5
                                tag: circle
                                vectorEffect: non-scaling-stroke
                                visible: '=(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: "#path4"
                            - 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: 4
                                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
                      display: flex
                      flex-direction: column
                      height: 110px
                      justify-content: center
                      margin-top: -10px
                      width: 110px
                  slots:
                    default:
                      - component: oh-icon
                        config:
                          color: orange
                          height: 110px
                          icon: sma_consumption_2
                      - component: Label
                        config:
                          style:
                            font-size: 20px
                            font-weight: bold
                            margin-top: -10px
                            text-align: center
                            width: 100px
                          text: =items[props.gesamtverbrauch].displayState
          - 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: -40px
                      width: 110px
                  slots: {}

And the output is:
Animation
Animation2

Basically I have changed the color of the animations. Perhaps I should use same unit for all the numbers.

I have to say this is really cool widget.

3 Likes

It is still the same on my setup, no connection from the solar pannels to consumers.
The 4 items are:

Number Wirkleistung_Bezug_W "[%d W]" (gEnergiezaehler) { channel="knx:device:bridge:Energiezaehler:Wirkleistung_Bezug_W" }
Number Wirkleistung_Einspeisung_W "[%d W]" (gEnergiezaehler) { channel="knx:device:bridge:Energiezaehler:Wirkleistung_Einspeisung_W" }
Number Wirkleistung "[%d W]"

Number SMA_TotalPac "[%.0f W]" (gSMA, gSMA_persist)

Wirkleistung is calculated in a rule:

	var power_solar = (SMA_TotalPac.state as Number).intValue
	var power_to_grid = (Wirkleistung_Einspeisung_W.state as Number).intValue
	var power_from_grid = (Wirkleistung_Bezug_W.state as Number).intValue
	var power = power_solar - power_to_grid + power_from_grid

	logInfo("Used power now", "Aktueller Verbrauch: " + power.toString)

	Wirkleistung.postUpdate(power)

How is your setup?

Thanks
Marco

I guess all item values should be of type float and that is the problem.

Ok, it was due to the wrong data types. Changing to parseInt and removing the battery state in calculations work.
Thank you for this cool widget

I always love when someone makes something look good. Those icons. :drooling_face:

Thank you for this great widget. I would also like to use it, but only get the power fed in and the power get from the supplier via the channels of the SMA binding.
Where do you get the total consumption and PV power from?

Thanks and greetings,
Tobias

If you measure production and import/export you can simply calculate missing parts.

Self consumption = yield - export
Total consumption = import + self

That’s pretty much it. It is same rule for power and energy. Only one thing to note - reporting (polling) cycle of devices which report data should be aligned so you calculate self and total from data in acceptable time window. Obviously power readings generated in such a way are not accurate but that’s all we need for visuals.

Thank you for the good advice. I had to read the total energy from the inverter too and was then able to calculate the consumption.

Inverter side is usually called a yield. You can divide all above in now (power), day/month/year/total. Just keep in mind that a total calculation can differ a lot if your electric meter and inverter were installed at different point of time.

Here is my take on this. It works for my situation and illustrates the current (multiplied by volts) situation and where the energy is going.

uid: House_Power_Widget
tags: []
props:
  parameters:
    - context: item
      description: Power to (or if negative from) grid
      label: Grid Power
      name: gridPower
      required: false
      type: TEXT
    - context: item
      description: Total power consumed
      label: Load Power
      name: loadPower
      required: false
      type: TEXT
    - context: item
      description: Solar power supplied
      label: Solar Power
      name: solarPower
      required: false
      type: TEXT
    - context: item
      description: Grid energy price in cents
      label: Grid energy tarrif
      name: gridOutCents
      required: false
      type: TEXT
    - context: item
      description: Grid Energy feed in tarrif in cents
      label: Feed in energy tarrif
      name: gridInCents
      required: false
      type: TEXT
    - context: item
      description: Car charger power
      label: Charger power
      name: carChargerPower
      required: false
      type: TEXT
    - context: item
      description: 1st Car battery level %
      label: Car 1 battery level
      name: car1BatteryLevel
      required: false
      type: TEXT
    - context: item
      description: 2nd Car battery level %
      label: Car 2 battery level
      name: car2BatteryLevel
      required: false
      type: TEXT
    - context: item
      description: Water heater switch
      label: Water heater
      name: waterHeater
      required: false
      type: TEXT
    - context: item
      description: Temperature of the water
      label: Water temperature
      name: waterTemp
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jul 8, 2022, 4:40:37 PM
component: f7-card
config: {}
slots:
  default:
    - component: f7-row
      config:
        style:
          align-items: center
      slots:
        default:
          - component: f7-row
            config:
              style:
                width: 100%
            slots:
              default:
                - component: oh-list
                  config:
                    style:
                      width: 100%
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: material:sunny
                          iconColor: '=(Number.parseFloat(items[props.solarpower].state.split(" ")[0]) > 0) ? "yellow" : "gray"'
                          title: =((Number.parseInt(items[props.solarpower].state.split(" ")[0])) / 1000).toFixed(1) + " kW"
          - component: f7-row
            config:
              style:
                width: 40%
            slots:
              default:
                - component: f7-row
                  config:
                    preserveAspectRatio: xMidYMid slice
                    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: M 30,0 L 30,10 C 30,45, 70,45, 70 45 L 100,45
                          fill: none
                          id: path_solar_grid
                          stroke: '=(Number.parseFloat(items[props.gridpower].state.split(" ")[0]) < 0) ? "rgb(31,96,196)" : "gray"'
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
                      - component: f7-row
                        config:
                          r: 4
                          fill: rgb(242,204,12)
                          strokeWidth: 20
                          tag: circle
                          vectorEffect: non-scaling-stroke
                          visible: =(Number.parseFloat(items[props.gridpower].state.split(" ")[0]) < 0)
                        slots:
                          default:
                            - component: f7-row
                              config:
                                calcMode: linear
                                dur: 4s
                                repeatCount: indefinite
                                tag: animateMotion
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      tag: mpath
                                      xlink:href: "#path_solar_grid"
                      - component: f7-row
                        config:
                          d: M 100,55 L 70 55 C 70,55, 30,55, 30,90 L 30,100
                          fill: none
                          id: path_grid_house
                          stroke: '=(Number.parseFloat(items[props.solarpower].state.split(" ")[0]) > 0) ? "rgb(31,96,196)" : "gray"'
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
                      - component: f7-row
                        config:
                          r: 4
                          fill: red
                          strokeWidth: 10
                          tag: circle
                          vectorEffect: non-scaling-stroke
                          visible: =(Number.parseFloat(items[props.gridpower].state.split(" ")[0]) > 0)
                        slots:
                          default:
                            - component: f7-row
                              config:
                                calcMode: linear
                                dur: 4s
                                repeatCount: indefinite
                                tag: animateMotion
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      tag: mpath
                                      xlink:href: "#path_grid_house"
                      - component: f7-row
                        config:
                          d: M 20,0 L 20,100
                          fill: none
                          id: path_solar_house
                          stroke: '=(Number.parseFloat(items[props.solarpower].state.split(" ")[0]) > 0) ? "rgb(242,204,12)" : "gray"'
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
                      - component: f7-row
                        config:
                          r: 4
                          fill: rgb(242,204,12)
                          strokeWidth: 10
                          tag: circle
                          vectorEffect: non-scaling-stroke
                          visible: =(Number.parseFloat(items[props.solarpower].state.split(" ")[0]) > 0)
                        slots:
                          default:
                            - component: f7-row
                              config:
                                calcMode: linear
                                dur: 4s
                                repeatCount: indefinite
                                tag: animateMotion
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      tag: mpath
                                      xlink:href: "#path_solar_house"
          - component: f7-row
            config:
              style:
                width: 60%
            slots:
              default:
                - component: oh-list
                  config:
                    style:
                      text-align: center
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: '=(Number.parseFloat(items[props.gridpower].state.split(" ")[0]) > 0) ? "material:arrow_circle_left" : "material:arrow_circle_right"'
                          iconColor: '=(Number.parseFloat(items[props.gridpower].state.split(" ")[0]) > 0) ? "orange" : "green"'
                          title: =((Number.parseInt(items[props.gridpower].state.split(" ")[0])) / 1000).toFixed(1) + " kW"
                      - component: oh-list-item
                        config:
                          icon: material:money
                          iconColor: '=(Number.parseFloat(items[props.gridpower].state.split(" ")[0]) > 0) ? "orange" : "green"'
                          title: '=((Number.parseFloat(items[props.gridpower].state.split(" ")[0]) > 0) ? ((Number.parseInt(items[props.gridpower].state.split(" ")[0])) / 100000 * Number.parseFloat(items[props.gridOutCents].state.split(" ")[0])).toFixed(2) : ((Number.parseInt(items[props.gridpower].state.split(" ")[0])) / -100000 * Number.parseFloat(items[props.gridInCents].state.split(" ")[0])).toFixed(2) ) + " €/h"'
          - component: f7-row
            config:
              style:
                width: 100%
            slots:
              default:
                - component: oh-list
                  config:
                    style:
                      width: 100%
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: material:home
                          title: =((Number.parseInt(items[props.loadPower].state.split(" ")[0])) / 1000).toFixed(1) + " kW"
          - component: f7-row
            config:
              style:
                width: 20%
            slots:
              default:
                - component: f7-row
                  config:
                    preserveAspectRatio: xMidYMid slice
                    height: auto
                    width: auto
                    tag: svg
                    viewBox: 0 0 50 80
                    xmlns: http://www.w3.org/2000/svg
                  slots:
                    default:
                      - component: f7-row
                        config:
                          d: M 25,40 L 50,40
                          fill: none
                          id: path_house_car_charger
                          stroke: '=((Number.parseInt(items[props.carChargerPower].state.split(" ")[0])) > 0) ? "rgb(196,22,42)" : "gray"'
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
                      - component: f7-row
                        config:
                          r: 4
                          fill: rgb(196,22,42)
                          strokeWidth: 10
                          tag: circle
                          vectorEffect: non-scaling-stroke
                          visible: =(Number.parseInt(items[props.carChargerPower].state.split(" ")[0])) > 0
                        slots:
                          default:
                            - component: f7-row
                              config:
                                calcMode: linear
                                dur: 1s
                                repeatCount: indefinite
                                tag: animateMotion
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      tag: mpath
                                      xlink:href: "#path_house_car_charger"
                      - component: f7-row
                        config:
                          d: M 25,0 L 25,80
                          fill: none
                          id: path_house_pass_car_charger
                          stroke: rgb(196,22,42)
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
          - component: f7-row
            config:
              style:
                width: 80%
                align-items: center
            slots:
              default:
                - component: oh-list
                  config:
                    style:
                      align-items: center
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: f7:car_fill
                          iconColor: '=(Number.parseInt(items[props.carChargerPower].state.split(" ")[0])) > 0 ? "" : "gray"'
                          title: =((Number.parseInt(items[props.carChargerPower].state.split(" ")[0])) / 1000).toFixed(1) + " kW"
                - component: oh-list
                  config:
                    style:
                      padding-left: 0px
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: ="battery-" + items[props.car1BatteryLevel].state.split(" ")[0]
                          title: =(items[props.car1BatteryLevel].displayState)
                      - component: oh-list-item
                        config:
                          icon: ="battery-" + items[props.car2BatteryLevel].state.split(" ")[0]
                          title: =(items[props.car2BatteryLevel].displayState)
          - component: f7-row
            config:
              style:
                width: 20%
            slots:
              default:
                - component: f7-row
                  config:
                    preserveAspectRatio: xMidYMid slice
                    height: auto
                    width: auto
                    tag: svg
                    viewBox: 0 0 50 50
                    xmlns: http://www.w3.org/2000/svg
                  slots:
                    default:
                      - component: f7-row
                        config:
                          d: M 25,0 L 25,25
                          fill: none
                          stroke: rgb(196,22,42)
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
                      - component: f7-row
                        config:
                          d: M 25,25 L 50,25
                          fill: none
                          id: path_house_water_heater
                          stroke: '=(items[props.waterHeater].state === "ON") ? "rgb(196,22,42)" : "gray"'
                          stroke-width: 2
                          tag: path
                          vector-effect: non-scaling-stroke
                      - component: f7-row
                        config:
                          r: 4
                          fill: rgb(196,22,42)
                          strokeWidth: 10
                          tag: circle
                          vectorEffect: non-scaling-stroke
                          visible: =(items[props.waterHeater].state === "ON")
                        slots:
                          default:
                            - component: f7-row
                              config:
                                calcMode: linear
                                dur: 1s
                                repeatCount: indefinite
                                tag: animateMotion
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      tag: mpath
                                      xlink:href: "#path_house_water_heater"
          - component: f7-row
            config:
              style:
                width: 80%
                align-items: center
            slots:
              default:
                - component: oh-list
                  config: {}
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: f7:drop_fill
                          iconColor: '=(items[props.waterHeater].state === "ON") ? "" : "gray"'
                          title: '=(items[props.waterHeater].state == "ON") ? "2.8 kW" : "0 kW"'
                - component: oh-list
                  config:
                    style:
                      padding-left: 0px
                  slots:
                    default:
                      - component: oh-list-item
                        config:
                          icon: material:thermostat
                          iconColor: '=(Number.parseFloat(items[props.waterTemp].state.split(" ")[0]) > 37) ? "orange" : "blue"'
                          title: =items[props.waterTemp].state

2 Likes