Ok so here is yet another Energy Flow widget. That can be adapted to any solar setup.
The base version was created by Uwe @debacher and I continued from that point. Many thanks for sharing your work.
Cleaned a bit, re did the flow lines to my liking and optimized the size for the 10" tablet.
I left plenty of his code untouched as I do not need those popups, but did not delete them as someone might want them and want to develop further from here.
uid: Energy_flow_Widget
tags:
- Version 0.1
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 Production (32064)
label: Solar power Now
name: item32064
required: false
type: TEXT
- context: item
description: Load Power (32080)
label: Load 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: Grid Power (37113)
label: Grid Power
name: item37113
required: false
type: TEXT
- context: item
description: Positive active electricity (37119)
label: Positive active electricity
name: item37119
required: false
type: TEXT
- context: item
description: Reverse active power (37121)
label: Reverse active power
name: item37121
required: false
type: TEXT
- context: item
description: Battery 1 SOC (37760)
label: Battery 1 SOC
name: item37760
required: false
type: TEXT
- context: item
description: Battery 2 SOC (37761)
label: Battery 2 SOC
name: item37761
required: false
type: TEXT
- context: item
description: Battery Charge/ Discharge power (37765)
label: Charge/ Discharge power
name: item37765
required: false
type: TEXT
- context: item
description: Daily Battery Charge (37784)
label: Daily Battery Charge
name: item37784
required: false
type: TEXT
- context: item
description: Daily Battery Consumption (37786)
label: Daily Battery Consumption
name: item37786
required: false
type: TEXT
- context: item
description: Daily Consumption From Grid
label: Daily Consumption From Grid
name: itemDailyConsumptionFromGrid
required: false
type: TEXT
- context: item
description: Daily Consumption by Load
label: Daily Consumption by Load
name: itemDailyConsumptionByLoad
required: false
type: TEXT
- context: item
description: Self Consumption
label: Selfconsumption ratio in %
name: itemSelfConsumption
required: false
type: TEXT
parameterGroups: []
timestamp: Feb 21, 2025, 10:47:03 AM
component: f7-card
config:
title: = props.title
padding-top: 0px
slots:
content:
- component: f7-block
config:
style:
margin: 0
padding: 0
padding-top: 0px
slots:
default:
- component: f7-block
config:
style:
--f7-block-font-size: 18px
--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
margin-top: -16px
slots:
default:
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 25px
justify-content: center
width: 150px
padding-left: 0px
padding-right: 0px
slots:
default:
- component: svg
config:
style:
height: 25px
width: 150px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M20,25 v-18 A5,5 0 0,1 25,2 h100, A5,5 0 0,1 130,7, v18
fill: none
id: label1
stroke: "#ffcc00"
stroke-width: 3
- component: Label
config:
style:
display: flex
white-space: nowrap
font-size: 16px
font-weight: 600
margin-top: -30px
justify-content: center
text: =items[props.item32114].displayState
- component: f7-block
config:
style:
align-items: center
border: 3px solid
border-color: "#ffcc00"
border-radius: 20px
display: flex
flex-direction: column
height: 150px
justify-content: center
width: 150px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: ="#info-solar"
slots:
default:
- component: oh-icon
config:
color: "#ffcc00"
height: 80px
icon: if:material-symbols:solar-power-outline-rounded
- component: oh-link
config:
action: popover
iconColor: green
iconF7: arrowtriangle_right_fill
iconSize: 20px
popoverOpen: ="#info-solar"
style:
font-size: 18px
font-weight: 500
white-space: nowrap
text: =items[props.item32064].displayState
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 140px
justify-content: center
width: 220px
padding-left: 0px
slots:
default:
- component: svg
config:
style:
height: 140px
width: 220px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M110,0 v25 A20,20 0 0,0 130,45 h95
fill: none
id: solarpath2
stroke: "#ffcc00"
stroke-width: 3
- component: circle
config:
fill: "#ffcc00"
r: 6
style:
stroke-width: 5
visible: "=(Math.abs(Number.parseFloat(items[props.item32064].state)) < 100) ?
false : true"
slots:
default:
- component: animateMotion
config:
dur: >-
=(Number.parseFloat(items[props.item32064].state)
> 15000) ? '1s' :
(Number.parseFloat(items[props.item32064].state) > 7000) ? '2s' :
(Number.parseFloat(items[props.item32064].state) > 4000) ? '3s' :
(Number.parseFloat(items[props.item32064].state) > 2000) ? '4s' :
(Number.parseFloat(items[props.item32064].state) > 1000) ? '6s' :
(Number.parseFloat(items[props.item32064].state) > 500) ? '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: "#solarpath2"
- component: path
config:
d: M110,140 v-25 A20,20 0 0,1 130,95 h95
fill: none
id: batterypath
stroke: "#ff780a"
stroke-width: 3
- component: circle
config:
fill: "#ff780a"
r: 6
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))
> 15000) ? '1s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 7000) ? '2s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 4000) ? '3s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 2000) ? '4s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 1000) ? '6s' :
(Math.abs(Number.parseFloat(items[props.item37765].state)) > 500) ? '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-block
config:
style:
align-items: center
border: 3px solid
border-color: "#ff780a"
border-radius: 20px
display: flex
flex-direction: column
height: 150px
justify-content: center
width: 150px
slots:
default:
- component: Label
config:
style:
white-space: nowrap
font-size: 18px
font-weight: 600
margin-top: 3px
margin-bottom: -6px
text: =items[props.item37760].displayState
- component: oh-link
config:
action: popover
align-item: center
popoverOpen: ="#info-batt"
slots:
default:
- component: oh-icon
config:
height: 80px
color: "#ff780a"
icon: "=(Number.parseFloat(items[props.item37760].state) < 21) ?
'if:tabler:battery' :
(Number.parseFloat(items[props.item3776\
0].state) < 41) ? 'if:tabler:battery-1'
:
(Number.parseFloat(items[props.item3776\
0].state) < 61) ? 'if:tabler:battery-2'
:
(Number.parseFloat(items[props.item3776\
0].state) < 81) ? 'if:tabler:battery-3'
: 'if:tabler:battery-4' "
margin-top: -5px
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) ?
'arrowtriangle_right_fill' :
'arrowtriangle_left_fill'"
iconSize: 18px
popoverOpen: ="#info-batt"
style:
font-size: 18px
font-weight: 600
white-space: nowrap
margin-top: -2px
margin-bottom: 6px
text: = items[props.item37765].displayState
- 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: 150px
justify-content: center
width: 150px
- component: f7-block
config:
style:
align-items: center
border: 3px solid grey
border-radius: 20px
display: flex
flex-direction: column
height: 150px
justify-content: center
width: 150px
slots:
default:
- component: oh-gauge
config:
min: 0
max: 100
size: 120
value: =items[props.itemSelfConsumption].state
type: semicircle
unit: "%"
valueText: =items[props.itemSelfConsumption].displayState
valueTextColor: white
valueFontSize: 18
textColor: white
borderColor: green
borderWidth: 16
borderBgColor: "#333333"
- component: oh-link
config:
action: popover
popoverOpen: ="#info-inverter"
style:
margin-top: 10px
font-size: 18px
white-space: nowrap
font-weight: 400
text: ='Self'
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
margin-top: -16px
slots:
default:
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 25px
justify-content: center
width: 150px
padding-left: 0px
padding-right: 0px
slots:
default:
- component: svg
config:
style:
height: 25px
width: 150px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M20,25 v-18 A5,5 0 0,1 25,2 h100, A5,5 0 0,1 130,7, v18
fill: none
id: label1
stroke: "#8ab8ff"
stroke-width: 3
- component: Label
config:
style:
display: flex
white-space: nowrap
font-size: 16px
font-weight: 600
margin-top: -30px
justify-content: center
text: =items[props.itemDailyConsumptionFromGrid].displayState
- component: f7-block
config:
style:
align-items: center
border: 3px solid
border-color: "#8ab8ff"
border-radius: 20px
display: flex
flex-direction: column
height: 150px
justify-content: center
width: 150px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: ="#info-grid"
slots:
default:
- component: oh-icon
config:
color: "#8ab8ff"
height: 80px
icon: if:hugeicons:factory
- component: oh-link
config:
action: popover
iconColor: "=(Number.parseFloat(items[props.item37113].state) < 0) ? 'red' :
'green'"
iconF7: "=(Number.parseFloat(items[props.item37113].state) > 0) ?
'arrowtriangle_right_fill' :
'arrowtriangle_left_fill'"
iconSize: 18px
popoverOpen: ="#info-grid"
style:
font-size: 18px
font-weight: 600
white-space: nowrap
text: = items[props.item37113].displayState
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 140px
justify-content: center
width: 220px
padding-left: 0px
slots:
default:
- component: svg
config:
style:
height: 140px
width: 220px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M110,0 v25 A20,20 0 0,1 90,45 h-90
fill: none
id: gridpath2
stroke: "#8ab8ff"
stroke-width: 3
- component: circle
config:
fill: "#8ab8ff"
r: 6
style:
stroke-width: 5
visible: "=(Math.abs(Number.parseFloat(items[props.item37113].state)) < 200 )
? false : true"
slots:
default:
- component: animateMotion
config:
dur: >-
=(Math.abs(Number.parseFloat(items[props.item37113].state))
> 15000) ? '1s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 7000) ? '2s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 4000) ? '3s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 2000) ? '4s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 1000) ? '6s' :
(Math.abs(Number.parseFloat(items[props.item37113].state)) > 500) ? '8s' :'16s'
keyPoints: "=Math.abs(Number.parseFloat(items[props.item37113].state) > 0) ?
'1;0' : '0;1'"
keyTimes: 0;1
repeatCount: indefinite
slots:
default:
- component: mpath
config:
xlink:href: "#gridpath2"
- component: path
config:
d: M110,140 v-25 A20,20 0 0,0 90,95 h-90
fill: none
id: loadpath2
stroke: "#4169E1"
stroke-width: 3
- component: circle
config:
fill: "#4169E1"
r: 6
style:
stroke-width: 4
visible: true
slots:
default:
- component: animateMotion
config:
dur: >-
=(Math.abs(Number.parseFloat(items[props.item32080].state))>
15000) ? '1s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)) > 7000) ? '2s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)) > 4000) ? '3s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)) > 2000) ? '4s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)) > 1000) ? '6s' :
(Math.abs(Number.parseFloat(items[props.item32080].state)) > 500) ? '8s' :'16s'
keyPoints: "=(Number.parseFloat(items[props.item32080].state) > 0) ? '1;0' :
'0;1'"
keyTimes: 0;1
repeatCount: indefinite
slots:
default:
- component: mpath
config:
xlink:href: "#loadpath2"
- component: f7-block
config:
style:
align-items: center
border: 3px solid
border-color: "#4169E1"
border-radius: 20px
display: flex
flex-direction: column
height: 150px
justify-content: center
width: 150px
slots:
default:
- component: oh-link
config:
action: popover
popoverOpen: ="#info-home"
slots:
default:
- component: oh-icon
config:
color: "#4169E1"
height: 80px
icon: if:hugeicons:electric-home-01
- component: oh-link
config:
action: popover
iconColor: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloa\
t(items[props.item37113].state)) > 0) ? 'red'
: 'green'"
iconF7: "=((Number.parseFloat(items[props.item32080].state)-Number.parseFloat(i\
tems[props.item37113].state)) > 0) ?
'arrowtriangle_right_fill' :
'arrowtriangle_left_fill'"
iconSize: 18px
popoverOpen: ="#info-home"
style:
font-size: 18px
font-weight: 600
white-space: nowrap
text: =items[props.item32080].displayState
- component: f7-block
config:
style:
align-items: center
flex-direction: column
height: 25px
justify-content: center
width: 150px
padding-left: 0px
padding-right: 0px
slots:
default:
- component: svg
config:
style:
height: 25px
width: 150px
xmlns: http://www.w3.org/2000/svg
slots:
default:
- component: path
config:
d: M20,0 v18 A5,5 0 0,0 25,24 h100, A5,5 0 0,0 130,18, v-18
fill: none
id: label1
stroke: "#4169E1"
stroke-width: 3
- component: Label
config:
style:
display: flex
white-space: nowrap
font-size: 16px
font-weight: 600
margin-top: -33px
justify-content: center
text: =items[props.itemDailyConsumptionByLoad].displayState
- 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