I used the past weeks to get a better understanding of ECharts. ECHARTS is a great tool even you have to experience a bit to get some functions working within Openhab.
I will now implement all charts with ECHARTS and will no longer use Grafana!
Attached the status of my charts on electricity power used in my house.
tab chart power used in current year (year=2025 [yellow+orange], year-1=2024 [green], year-2=2023 [blue]):
Data Table view:
If you use widgets to create your chart, the time is displayed correct … which is currently not the case if you use the chart tool in pages).
tab chart power used in previous year 2024 (year=2024 [yellow, orange], year-1=2023 [green]):
pie chart power used the past 3 years:
the widget used for the charts in a tabbed page
uid: Card_Verbrauch
tags:
- BG
props:
parameters:
- description: A text prop
label: Prop 1
name: prop1
required: false
type: TEXT
- context: item
description: Strom Verbrauch
label: Item
name: prop_itemVerbrauch
required: false
type: TEXT
- context: item
description: Strom Verbrauch Jahr
label: Item
name: prop_itemVerbrauchJahr
required: false
type: TEXT
- context: item
description: Strom Monatserwartung
label: Item
name: prop_itemkWh20xxMonatserwartung
required: false
type: TEXT
- context: item
description: Strom Monatdurchschnitt
label: Item
name: prop_itemkWh20xxMonatsdurchschnitt
required: false
type: TEXT
- context: item
description: Temperatur Item
label: Item
name: prop_itemTemperatur
required: false
type: TEXT
parameterGroups: []
timestamp: Feb 10, 2025, 11:05:25 AM
component: f7-card
config:
backdrop: false
class:
- no-padding
expandable: false
style:
--f7-theme-color: var(--f7-text-color)
border-radius: var(--f7-card-expandable-border-radius)
box-shadow: var(--f7-card-expandable-box-shadow)
height: 515px
margin-bottom: 0px
margin-left: 0px
margin-right: 0px
margin-top: 0px
width: 100%
swipeToClose: false
slots:
default:
- component: f7-segmented
config:
class: segmented-round
style:
bottom: -15px
height: 30px
left: 0px
position: absolute
width: 100%
z-index: 2
slots:
default:
- component: oh-button
config:
action: variable
actionVariable: varPeriod
actionVariableValue: year
iconSize: 20px
outline: true
style:
--f7-button-bg-color: "=(vars.varPeriod == 'year' || vars.varPeriod ==
undefined) ? 'transparent' : '#f0f0f0' "
--f7-button-hover-bg-color: "#e7f3fe"
--f7-button-pressed-bg-color: "#9dcefb"
height: 100%
width: 100%
text: Jahr
textColor: "=(vars.varPeriod == 'year' || vars.varPeriod == undefined) ? 'red' :
'black' "
visible: true
- component: oh-button
config:
action: variable
actionVariable: varPeriod
actionVariableValue: total
iconSize: 20px
outline: true
style:
--f7-button-bg-color: "=(vars.varPeriod == 'total') ? 'transparent' : '#f0f0f0' "
--f7-button-hover-bg-color: "#e7f3fe"
--f7-button-pressed-bg-color: "#9dcefb"
height: 100%
width: 100%
text: ∑ Jahre
textColor: "=(vars.varPeriod == 'total' ) ? 'red' : 'black' "
visible: true
- component: oh-button
config:
action: variable
actionVariable: varPeriod
actionVariableValue: month
iconSize: 20px
outline: true
style:
--f7-button-bg-color: "=(vars.varPeriod == 'month') ? 'transparent' : '#f0f0f0' "
--f7-button-hover-bg-color: "#e7f3fe"
--f7-button-pressed-bg-color: "#9dcefb"
height: 100%
width: 100%
text: Monat
textColor: "=(vars.varPeriod == 'month' ) ? 'red' : 'black' "
- component: oh-button
config:
action: variable
actionVariable: varPeriod
actionVariableValue: week
iconSize: 20px
outline: true
style:
--f7-button-bg-color: "=(vars.varPeriod == 'week' ) ? 'transparent' : '#f0f0f0' "
--f7-button-hover-bg-color: "#e7f3fe"
--f7-button-pressed-bg-color: "#9dcefb"
height: 100%
width: 100%
text: Woche
textColor: "=(vars.varPeriod == 'week' ) ? 'red' : 'black' "
- component: oh-button
config:
action: variable
actionVariable: varPeriod
actionVariableValue: day
iconSize: 20px
outline: true
style:
--f7-button-bg-color: "=(vars.varPeriod == 'day' ) ? 'transparent' : '#f0f0f0' "
--f7-button-hover-bg-color: "#e7f3fe"
--f7-button-pressed-bg-color: "#9dcefb"
height: 100%
width: 100%
text: Tag
textColor: "=(vars.varPeriod == 'day' ) ? 'red' : 'black'"
- component: oh-chart
config:
height: 100%
options:
backgroundColor: transparent
sidebar: false
visible: "=vars.varPeriod == 'total' ? true : false"
slots:
title:
- component: oh-chart-title
config:
show: true
text: Strom-Jahresvergleich
top: 0
left: 170px
legend:
- component: oh-chart-legend
config:
left: center
orient: horizontal
show: true
bottom: 25px
width: 600
grid:
- component: oh-chart-grid
config:
height: 70%
includeLabels: true
left: 70
right: 70
show: false
top: 60
series:
- component: oh-data-series
config:
type: pie
avoidLabelOverlap: true
labelLine:
length: 20
radius:
- 25%
- 55%
center:
- 50%
- 70%
startAngle: 180
endAngle: 360
data:
- value: =items.Strom2023.state
name: "2023"
label:
show: true
formatter: |-
{c|{c} kWh/Jahr
472 kWh/Monat
15.5 kWh/Tag}
backgroundColor: "#F6F8FC"
borderColor: "#8C8D8E"
borderWidth: 1
borderRadius: 4
padding: 2
rich:
c:
color: "#4C5058"
fontSize: 12
fontWeight: bold
lineHeight: 20
- value: =items.Strom2024.state
name: "2024"
label:
show: true
formatter: |-
{c|{c} kWh/Jahr
468 kWh/Monat
15.3 kWh/Tag}
backgroundColor: "#F6F8FC"
borderColor: "#8C8D8E"
borderWidth: 1
borderRadius: 4
padding: 2
rich:
c:
color: "#4C5058"
fontSize: 12
fontWeight: bold
lineHeight: 20
- value: =items.Strom2025.state
name: "2025"
label:
show: true
formatter: "{c|{c} kWh/Jahr}"
backgroundColor: "#F6F8FC"
borderColor: "#8C8D8E"
borderWidth: 1
borderRadius: 4
padding: 2
rich:
c:
color: "#4C5058"
fontSize: 12
fontWeight: bold
lineHeight: 20
tooltip:
- component: oh-chart-tooltip
config:
show: true
- component: oh-chart
config:
chartType: isoWeek
height: 100%
options:
backgroundColor: transparent
sidebar: true
visible: "=vars.varPeriod == 'week' ? true : false"
slots:
grid:
- component: oh-chart-grid
config:
height: 70%
includeLabels: true
left: 70
right: 70
show: false
top: 70
legend:
- component: oh-chart-legend
config:
left: 70
orient: horizontal
show: true
bottom: 25px
width: 600
series:
- component: oh-aggregate-series
config:
aggregationFunction: diff_last
color: yellow
dimension1: isoWeekday
gridIndex: 0
id: 0
item: =props.prop_itemVerbrauch
markLine:
data:
- type: average
label:
shom: true
position: end
formatter: "{c} kWh"
backgroundColor: "#FF6B22"
padding: 2
name: Strom-Verbrauch
type: bar
xAxisIndex: 0
yAxisIndex: 0
label:
show: false
formatter: =v=>Number.parseFloat(v.data[1]).toFixed(1) + " kWh"
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#673AB7"
dimension1: isoWeekday
gridIndex: 0
id: 1
item: =props.prop_itemTemperatur
lineStyle:
width: 2
type: dashed
name: Temperatur
type: line
step: middle
xAxisIndex: 0
yAxisIndex: 1
symbol: circle
symbolSize: 5
tooltip:
- component: oh-chart-tooltip
config:
confine: true
show: true
trigger: axis
xAxis:
- component: oh-category-axis
config:
categoryType: week
gridIndex: 0
nameGap: 22
nameLocation: center
monthFormat: short
weekdayFormat: short
yAxis:
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} kWh"
gridIndex: 0
nameGap: 40
nameLocation: center
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} °C"
gridIndex: 0
nameGap: 40
nameLocation: center
scale: true
show: true
- component: oh-chart
config:
chartType: month
height: 100%
options:
backgroundColor: transparent
sidebar: true
visible: "=vars.varPeriod == 'month' ? true : false"
slots:
calendar: []
grid:
- component: oh-chart-grid
config:
height: 70%
includeLabels: true
left: 70
right: 70
show: false
top: 70
legend:
- component: oh-chart-legend
config:
left: 70
orient: horizontal
show: true
bottom: 25px
width: 600
series:
- component: oh-aggregate-series
config:
aggregationFunction: diff_last
color: yellow
dimension1: date
gridIndex: 0
id: 0
item: =props.prop_itemVerbrauch
markLine:
data:
- type: average
label:
shom: true
position: end
formatter: "{c} kWh"
backgroundColor: "#FF6B22"
padding: 2
markPoint:
data:
- name: min
type: min
- name: max
type: max
name: Strom-Verbrauch
type: bar
xAxisIndex: 0
yAxisIndex: 0
label:
show: false
formatter: =v=>Number.parseFloat(v.data[1]).toFixed(1) + " kWh"
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#673AB7"
dimension1: date
gridIndex: 0
id: 1
item: =props.prop_itemTemperatur
lineStyle:
width: 2
type: dashed
name: Temperatur
type: line
step: middle
xAxisIndex: 0
yAxisIndex: 1
symbol: circle
symbolSize: 5
tooltip:
- component: oh-chart-tooltip
config:
confine: true
show: true
trigger: axis
xAxis:
- component: oh-category-axis
config:
categoryType: month
gridIndex: 0
nameGap: 22
nameLocation: center
weekdayFormat: short
monthFormat: short
yAxis:
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} kWh"
gridIndex: 0
nameGap: 40
nameLocation: center
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} °C"
gridIndex: 0
nameGap: 40
nameLocation: center
scale: true
- component: oh-chart
config:
chartType: day
height: 100%
options:
backgroundColor: transparent
sidebar: true
visible: "=vars.varPeriod == 'day' ? true : false"
slots:
calendar: []
grid:
- component: oh-chart-grid
config:
height: 70%
includeLabels: true
left: 70
right: 70
show: false
top: 70
legend:
- component: oh-chart-legend
config:
left: 70
orient: horizontal
show: true
bottom: 25px
width: 600
series:
- component: oh-aggregate-series
config:
aggregationFunction: diff_last
color: yellow
dimension1: hour
gridIndex: 0
id: 0
item: =props.prop_itemVerbrauch
markLine:
data:
- type: average
label:
shom: true
position: end
formatter: "{c} kWh"
backgroundColor: "#FF6B22"
padding: 2
markPoint:
data:
- name: min
type: min
- name: max
type: max
name: Strom-Verbrauch
type: bar
xAxisIndex: 0
yAxisIndex: 0
label:
show: false
formatter: =v=>Number.parseFloat(v.data[1]).toFixed(1) + " kWh"
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#673AB7"
dimension1: hour
gridIndex: 0
id: 1
item: =props.prop_itemTemperatur
lineStyle:
width: 2
type: dashed
name: Temperatur
type: line
step: middle
xAxisIndex: 0
yAxisIndex: 1
symbol: circle
symbolSize: 5
tooltip:
- component: oh-chart-tooltip
config:
confine: true
show: true
trigger: axis
xAxis:
- component: oh-category-axis
config:
categoryType: day
gridIndex: 0
nameGap: 22
nameLocation: center
monthFormat: short
weekdayFormat: short
yAxis:
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} kWh"
gridIndex: 0
nameGap: 40
nameLocation: center
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} °C"
gridIndex: 0
nameGap: 40
nameLocation: center
scale: true
- component: oh-chart
config:
periodVisible: true
chartType: year
height: 100%
options:
backgroundColor: transparent
sidebar: false
visible: "(=vars.varPeriod == 'year' || vars.varPeriod == undefined) ? true :
false"
slots:
calendar: []
title:
- component: oh-chart-title
config:
show: true
subtext: "2023: 5664 kWh - 2024: 5614 kWh"
top: 0
left: 170px
grid:
- component: oh-chart-grid
config:
height: 70%
includeLabels: true
left: 75
right: 75
show: false
top: 70
legend:
- component: oh-chart-legend
config:
left: 25
orient: horizontal
show: true
bottom: 25px
width: 600
series:
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#007AFF"
dimension1: month
gridIndex: 0
item: =props.prop_itemVerbrauchJahr
service: inmemory
noBoundary: true
offsetAmount: 2
offsetUnit: year
lineStyle:
width: 4
markLine:
data:
- type: average
symbol: none
label:
shom: true
position: start
formatter: "{c} kWh"
backgroundColor: "#007AFF"
padding: 2
name: Jahr-2
type: line
step: middle
xAxisIndex: 0
yAxisIndex: 0
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#009688"
dimension1: month
gridIndex: 0
item: =props.prop_itemVerbrauchJahr
service: inmemory
noBoundary: true
offsetAmount: 1
offsetUnit: year
lineStyle:
width: 4
markLine:
data:
- type: average
symbol: none
label:
shom: true
position: middle
formatter: "{c} kWh"
backgroundColor: "#009688"
padding: 2
name: Jahr-1
type: line
step: middle
xAxisIndex: 0
yAxisIndex: 0
- component: oh-aggregate-series
config:
aggregationFunction: average
color: yellow
dimension1: month
gridIndex: 0
id: 0
item: =props.prop_itemVerbrauchJahr
service: inmemory
noBoundary: true
markLine:
data:
- type: average
symbol: circle
label:
shom: true
formatter: "{c} kWh"
backgroundColor: "#FF6B22"
padding: 2
name: Jahr
type: bar
xAxisIndex: 0
yAxisIndex: 0
- component: oh-aggregate-series
config:
aggregationFunction: average
color: yellow
dimension1: month
gridIndex: 0
item: =props.prop_itemkWh20xxMonatserwartung
service: inmemory
noBoundary: true
lineStyle:
width: 2
markPoint:
data:
- name: max
type: max
name: Monatserwartung
type: line
xAxisIndex: 0
yAxisIndex: 0
symbol: arrow
symbolSize: 20
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#FF6B22"
dimension1: month
gridIndex: 0
item: =props.prop_itemkWh20xxMonatsdurchschnitt
service: inmemory
noBoundary: true
lineStyle:
width: 2
markLine:
data:
- type: average
symbol: circle
label:
shom: true
formatter: "{c} kWh"
backgroundColor: "#FF6B22"
padding: 2
name: Monat-Ø
type: line
xAxisIndex: 0
yAxisIndex: 0
- component: oh-aggregate-series
config:
aggregationFunction: average
color: "#673AB7"
dimension1: month
gridIndex: 0
id: 1
item: =props.prop_itemTemperatur
noBoundary: true
lineStyle:
width: 2
type: dashed
name: Temperatur
type: line
step: middle
xAxisIndex: 0
yAxisIndex: 1
symbol: circle
symbolSize: 5
tooltip:
- component: oh-chart-tooltip
config:
confine: true
show: true
trigger: axis
toolbox:
- component: oh-chart-toolbox
config:
presetFeatures:
- restore
- dataView
- magicType
top: 8
left: 10
show: true
xAxis:
- component: oh-category-axis
config:
categoryType: year
gridIndex: 0
nameGap: 20
nameLocation: center
monthFormat: short
yAxis:
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} kWh"
gridIndex: 0
nameGap: 30
nameLocation: center
- component: oh-value-axis
config:
axisLabel:
formatter: "{value} °C"
gridIndex: 0
nameGap: 40
nameLocation: center
scale: true
Rule which runs every night to update the charts and to calculate the expected power value for current month an the average monthly power value
//
// Update current month value and expected value at end of current month
//
// Format DayDatum 'YYYY-MM-DD'
var datum = items.getItem('DayDatum').state;
var datum2 = datum;
// get day, month, Year from date
var jahr = datum.substring(0, 4);
jahr = parseInt(jahr);
console.log(jahr);
// is year a leap year?
var schaltjahr = 0 ;
var jahrxx = parseInt(jahr / 4) ;
jahrxx = jahrxx * 4 ;
if (jahr == jahrxx) {schaltjahr = 1} ;
//
var monat = datum.substring(5, 7);
monat = parseInt(monat);
console.log(monat);
var tag = datum.substring(8, 10);
tag = parseInt(tag);
console.log(tag);
// I use the 28th of month in chart and widget for month total
datum2 = datum.substring(0, 8) + '28T23:59';
console.log(datum2);
// last day of month
var last = 31 ;
if (monat == 4 || monat == 6 || monat == 9 || monat == 11) {last = 30} ;
// last day of February - leap year?
if (monat == 2) {
last = 28 ;
if (schaltjahr == 1) {last = 29 } ;
}
// DayMonatswert = kWh of current month
var monatswert = items.getItem('DayMonatswert').state;
console.log(monatswert);
// monatserwartung = expected kWh at the end of current month
monatserwartung = monatswert * last / tag ;
monatserwartung = Math.round(monatserwartung) ;
console.log(monatserwartung);
monatswert = monatswert + ' kWh';
monatserwartung = monatserwartung + ' kWh';
// in January the average kWh rate over all month in year is equal to expected kWh rate of actual month
if (monat == 1) {
monatsdurchschnitt = monatserwartung ;
} else {
// in Feb ... Dez: calculate the average kWh value over all month in year
// calculate date until actual Date
var monatstage = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ] ;
if (schaltjahr == 1) { monatstage = [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]} ;
// days of actual month
var tage = tag ;
console.log(tage);
var j = monat - 1;
for (i=0; i < monatstage.length; i++) {
if (i >= j) {break};
tage = tage + monatstage[i];
console.log(tage);
}
// DayJahreswert = kWh since 01 Jan of actual year
var jahreswert = items.getItem('DayJahreswert').state;
// monatsdurchschnitt = the average kWh value over all month in year
monatsdurchschnitt = jahreswert * last / tage ;
monatsdurchschnitt = Math.round(monatsdurchschnitt) ;
console.log(monatsdurchschnitt);
monatsdurchschnitt = monatsdurchschnitt + ' kWh';
}
//
// timeSeries20xx for widget
// Replace in TimeSeries current month with month value 'Monatswert' - with policy REPLACE
var timeSeries20xx = new items.TimeSeries('REPLACE');
timeSeries20xx.add(time.toZDT(datum2), Quantity(monatswert)) ;
// Persist the TimeSeries for the Item 'kWh20xx' using the InMemory persistence service
items.getItem('kWh20xx').persistence.persist(timeSeries20xx, 'inmemory');
//
// Display the expected kWh value at the end of the month 'Monatserwartung' as seperate value
var timeSeries20yy = new items.TimeSeries('REPLACE');
timeSeries20yy.add(time.toZDT(datum2), Quantity(monatserwartung)) ;
// Persist the TimeSeries for the Item 'kWh20xxErwartung' using the InMemory persistence service
items.getItem('kWh20xxErwartung').persistence.persist(timeSeries20yy, 'inmemory');
//
// Display the average kWh value over all month of current year 'Monatdurchschnitt' as seperate value
var timeSeries20dd = new items.TimeSeries('REPLACE');
timeSeries20dd.add(time.toZDT(datum2), Quantity(monatsdurchschnitt)) ;
// Persist the TimeSeries for the Item 'kWh20xxErwartung' using the InMemory persistence service
items.getItem('kWh20xxMonatsdurchschnitt').persistence.persist(timeSeries20dd, 'inmemory');
//
I hope this may help some of you to create good charts