Skip to content

Commit df8c960

Browse files
committed
Dashboards: add Nordpool energy chart card, README and example image
- Add card_nordpool_energy_chart.yaml: configurable Lovelace chart (custom:config-template-card + custom:apexcharts-card) - Displays today/tomorrow Nordpool prices, grid import and battery SOC - Dynamic price calculations, color thresholds, formatted axes and grouping - Add README with installation, configuration and disable instructions - Add example screenshot HA_energy_chart.jpg
1 parent 2222602 commit df8c960

File tree

3 files changed

+355
-0
lines changed

3 files changed

+355
-0
lines changed
147 KB
Loading
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# This code should be copy-pasted into a lovelace card.
2+
# Requires the custom:config-template-card, custom:apexcharts-card, custom:nordpool-card and custom:congif-template-card to be installed.
3+
4+
type: custom:config-template-card
5+
variables:
6+
nordpool_sensor: '''sensor.nordpool'''
7+
grid_import_sensor: '''sensor.sigen_daily_grid_energy_import'''
8+
battery_soc_sensor: '''sensor.sigen_energy_storage_system_soc'''
9+
additional_cost_template: ''' *1 '''
10+
decimals_in_prices: 0
11+
decimals_in_energy: 1
12+
chart_price_cutoff_coerficient: 0.1
13+
price_y_axis_unit: '''öre'''
14+
energy_y_axis_unit: '''kWh'''
15+
price_overview_unit: '''öre/kWh'''
16+
chart_title: '''Electricity prices today'''
17+
total_purchease_price_text: '''Total purchease price'''
18+
grid_import_text: '''Grid Import today'''
19+
battery_soc_text: '''Battery charge'''
20+
lowest_price_text: '''Lowest future price'''
21+
highest_price_text: '''Highest future price'''
22+
price_now_text: '''Current price'''
23+
now_text: ''''''
24+
get_chart_cutoff: |
25+
(_min_price, _max_price, _coefficient) => {
26+
if (_min_price <= 0) {
27+
return "auto";
28+
}
29+
return _min_price - ((_max_price-_min_price) * _coefficient)
30+
}
31+
get_total_max_nordpool_price: |
32+
(_nordpool_sensor, _additional_cost_template) => {
33+
return states[_nordpool_sensor].attributes.raw_today.concat(states[_nordpool_sensor].attributes.raw_tomorrow).map(data=> eval("data.value"+_additional_cost_template)).reduce((a, b) => Math.max(a, b), -Infinity)
34+
}
35+
get_total_min_nordpool_price: |
36+
(_nordpool_sensor, _additional_cost_template) => {
37+
return states[_nordpool_sensor].attributes.raw_today.concat(states[_nordpool_sensor].attributes.raw_tomorrow).map(data=> eval("data.value"+_additional_cost_template)).reduce((a, b) => Math.min(a, b), Infinity)
38+
}
39+
get_low_bottom_threshold_nordpool_price: |
40+
(_min, _max) => {
41+
return parseFloat(_min) + parseFloat(((_max) - parseFloat(_max)) /10)
42+
}
43+
get_low_threshold_nordpool_price: |
44+
(_min, _max) => {
45+
return parseFloat(_min) + parseFloat(((_max) - parseFloat(_min)) /3)
46+
}
47+
get_high_threshold_nordpool_price: |
48+
(_min, _max) => {
49+
return parseFloat(_min)
50+
+ (( parseFloat(_max) - parseFloat(_min) ) /3) * 2
51+
}
52+
get_high_top_threshold_nordpool_price: |
53+
(_min, _max) => {
54+
return parseFloat(_min) + parseFloat(((_max) - parseFloat(_min)) /10)*9
55+
}
56+
get_total_prices: |
57+
_cost_template => {
58+
return "return (entity.attributes.raw_today.map((start, index) => {return [new Date(start['start']).getTime(), entity.attributes.raw_today[index]['value']"
59+
+ _cost_template
60+
+ "];})).concat(entity.attributes.raw_tomorrow.map((start, index) => {return [new Date(start['start']).getTime(),entity.attributes.raw_tomorrow[index]['value'] "
61+
+ _cost_template + "];}));"
62+
}
63+
get_future_total_prices: |
64+
_cost_template => {
65+
return "const now = new Date().getTime() - 3600000;"
66+
+ "const futureData = entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow).filter(data"
67+
+ " => new Date(data.start).getTime() >= now);"
68+
+ "return futureData.map((data, index) => {"
69+
+ "return [new Date(data.start).getTime(), futureData[index].value"
70+
+ _cost_template + "];});"
71+
}
72+
entities:
73+
- ${nordpool_sensor}
74+
- ${battery_soc_sensor}
75+
- ${grid_import_sensor}
76+
card:
77+
type: custom:apexcharts-card
78+
graph_span: >-
79+
${ states[nordpool_sensor].attributes.raw_today.length +
80+
states[nordpool_sensor].attributes.raw_tomorrow.length + 'h' }
81+
yaxis:
82+
- id: y-price
83+
min: >-
84+
${get_chart_cutoff(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),get_total_max_nordpool_price(nordpool_sensor,additional_cost_template),chart_price_cutoff_coerficient)}
85+
max: auto
86+
apex_config:
87+
opposite: false
88+
forceNiceScale: true
89+
decimalsInFloat: ${decimals_in_prices}
90+
labels:
91+
formatter: >
92+
${"EVAL:function(value) {return value.toFixed(" + decimals_in_prices
93+
+ ") + ' " + price_y_axis_unit + "' }"
94+
- id: y-energy
95+
min: 0
96+
max: auto
97+
apex_config:
98+
opposite: true
99+
forceNiceScale: true
100+
decimalsInFloat: ${decimals_in_energy}
101+
labels:
102+
formatter: |
103+
${"EVAL:function(value) {return value.toFixed("
104+
+ decimals_in_energy
105+
+ ") + ' " + energy_y_axis_unit + "' }"
106+
- id: y-SOC
107+
show: false
108+
min: 0
109+
max: 100
110+
apex_config:
111+
chart:
112+
height: 340px
113+
legend:
114+
show: false
115+
title:
116+
floating: false
117+
align: center
118+
style:
119+
fontSize: 20px
120+
fontWeight: bold
121+
xaxis:
122+
labels:
123+
datetimeFormatter:
124+
hour: HH:mm
125+
show:
126+
last_updated: true
127+
experimental:
128+
color_threshold: true
129+
header:
130+
title: ${chart_title}
131+
show: true
132+
show_states: true
133+
colorize_states: true
134+
span:
135+
start: day
136+
now:
137+
show: true
138+
label: ${now_text}
139+
series:
140+
- entity: ${nordpool_sensor}
141+
yaxis_id: y-price
142+
name: ${total_purchease_price_text}
143+
offset: '-30min'
144+
float_precision: ${decimals_in_prices}
145+
show:
146+
extremas: false
147+
in_header: false
148+
type: column
149+
data_generator: ${get_total_prices(additional_cost_template)}
150+
color_threshold:
151+
- value: -1000
152+
color: '#00f738'
153+
- value: >-
154+
${get_low_bottom_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
155+
get_total_max_nordpool_price(nordpool_sensor,additional_cost_template)
156+
)}
157+
color: '#12A141'
158+
- value: >-
159+
${get_low_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
160+
get_total_max_nordpool_price(nordpool_sensor,additional_cost_template))}
161+
color: '#F3DC0C'
162+
- value: >-
163+
${get_high_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
164+
get_total_max_nordpool_price(nordpool_sensor,
165+
additional_cost_template))}
166+
color: '#E76821'
167+
- value: >-
168+
${get_high_top_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
169+
get_total_max_nordpool_price(nordpool_sensor,additional_cost_template))}
170+
color: '#DC182F'
171+
- entity: ${grid_import_sensor}
172+
yaxis_id: y-energy
173+
name: Grid import Chart
174+
color: '#FF4646'
175+
curve: stepline
176+
float_precision: ${decimals_in_energy}
177+
show:
178+
extremas: false
179+
in_header: false
180+
type: line
181+
stroke_width: 3
182+
opacity: 0.75
183+
extend_to: false
184+
unit: ${energy_y_axis_unit}
185+
group_by:
186+
func: delta
187+
duration: 1h
188+
- entity: ${grid_import_sensor}
189+
name: ${grid_import_text}
190+
color: '#FF4646'
191+
float_precision: ${decimals_in_energy}
192+
show:
193+
in_chart: false
194+
in_header: raw
195+
unit: ${energy_y_axis_unit}
196+
- entity: ${battery_soc_sensor}
197+
yaxis_id: y-SOC
198+
name: ${battery_soc_text}
199+
color: '#7a50d9'
200+
float_precision: 0
201+
show:
202+
extremas: false
203+
in_header: raw
204+
header_color_threshold: true
205+
type: line
206+
stroke_width: 3
207+
opacity: 0.75
208+
extend_to: false
209+
group_by:
210+
func: avg
211+
duration: 1h
212+
- entity: ${nordpool_sensor}
213+
color: '#7bff00'
214+
float_precision: ${decimals_in_prices}
215+
name: ${lowest_price_text}
216+
unit: ${price_overview_unit}
217+
show:
218+
in_chart: false
219+
legend_value: false
220+
group_by:
221+
func: min
222+
duration: 2d
223+
data_generator: ${get_future_total_prices(additional_cost_template)}
224+
- entity: ${nordpool_sensor}
225+
name: ${price_now_text}
226+
type: column
227+
unit: ${price_overview_unit}
228+
show:
229+
in_header: before_now
230+
in_chart: false
231+
header_color_threshold: true
232+
float_precision: ${decimals_in_prices}
233+
data_generator: ${get_future_total_prices(additional_cost_template)}
234+
color_threshold:
235+
- value: -1000
236+
color: '#00f738'
237+
- value: >-
238+
${get_low_bottom_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
239+
get_total_max_nordpool_price(nordpool_sensor,additional_cost_template)
240+
)}
241+
color: '#12A141'
242+
- value: >-
243+
${get_low_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
244+
get_total_max_nordpool_price(nordpool_sensor,additional_cost_template))}
245+
color: '#F3DC0C'
246+
- value: >-
247+
${get_high_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
248+
get_total_max_nordpool_price(nordpool_sensor,
249+
additional_cost_template))}
250+
color: '#E76821'
251+
- value: >-
252+
${get_high_top_threshold_nordpool_price(get_total_min_nordpool_price(nordpool_sensor,additional_cost_template),
253+
get_total_max_nordpool_price(nordpool_sensor,additional_cost_template))}
254+
color: '#DC182F'
255+
- entity: ${nordpool_sensor}
256+
color: '#DC182F'
257+
float_precision: ${decimals_in_prices}
258+
name: ${highest_price_text}
259+
unit: ${price_overview_unit}
260+
show:
261+
in_chart: false
262+
legend_value: false
263+
group_by:
264+
func: max
265+
duration: 2d
266+
data_generator: ${get_future_total_prices(additional_cost_template)}
267+
layout_options:
268+
grid_columns: full
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Energy price chart using nordpool prices.
2+
3+
This is my atempt at a chart displaying todays prices and tomorows prices when available along with imported energy.
4+
- Upper and Lower price threshold is calculated for today+tomorows ***comming*** price.
5+
- Orange is upper 3:rd of the pricing interval.
6+
- Green is lower 3:rd of the prices interval.
7+
- Red is the upper 10% of the pricing interval.
8+
9+
![alt text](HA_energy_chart.jpg)
10+
11+
# Pre-requirements
12+
13+
This has not been tested on HA earlier than 2024.9 and requires the following HACS addons:
14+
15+
- Nordpool - For getting energy prices.
16+
- Apex-Charts card
17+
- Config Template Card Card - For scripting inside the chart card.
18+
- lovelace-card-mod - For styling.
19+
20+
The Nordpool entity should be changed from the default name to just "nordpool" as per the components installation instructions.
21+
22+
# Installation
23+
- Copy - paste the raw card code from `card_nordpool_energy_chart.yaml` to your a new card on you lovelace dashboard. Add any card and replace the raw code that you can edit by clicking on the *SHOW CODE EDITOR* in the bottom left.
24+
25+
26+
# Configuration
27+
28+
Configure your Nordpool integration.
29+
30+
In the chart-card you should change the following variables to suit your needs:
31+
32+
- Check so the `nordpool_sensor:` name is correct.
33+
- The Grid import sensor name: `grid_import_sensor:` should be changed if not using Sigenergy system.
34+
- The
35+
- Change `currency` if needed.
36+
- `chart_price_cutoff_coerficient` from 0 to 1 to show more or less barheight under the minimum value.
37+
- `decimals_in_prices` and `decimals_in_energy` to indicate how many decimals to show.
38+
- `additional_cost_template` is used for adding extra costs if not added by nordpol sensor. It should be empty or start with a arithmetic operator.
39+
- Example:
40+
- additional_cost_template: '''*1.25 +(27.2 + 42.8 + 3.04 + 2.19 + 1.4) *1.25'''
41+
- Ads 25% taxes and then other costs as specified by the grid operator with more taxes.
42+
- Change the Now text to any label you want to apear `now_text: '''Now'''` besides the line showing current time in the chart.
43+
- And any of the texts below to suit your language or prefferences.
44+
45+
# Disable the Grid or Battery chart
46+
47+
## Disable the Grid chart
48+
49+
1. Comment out the update entity:
50+
```
51+
entities:
52+
- ${nordpool_sensor}
53+
- ${battery_soc_sensor}
54+
# - ${grid_import_sensor}
55+
```
56+
57+
2. Delete the rows starting with ```- entity: ${grid_import_sensor}``` and all rows under it until the next ```- entity:```. There are currently two such sections.
58+
59+
## Disable the Battery SOC
60+
61+
1. Comment out the update entity:
62+
```
63+
entities:
64+
- ${nordpool_sensor}
65+
# - ${battery_soc_sensor}
66+
- ${grid_import_sensor}
67+
```
68+
69+
2. Delete the row starting with ```- entity: ${battery_soc_sensor}``` and all rows under it until the next ```- entity:```. There is currently only one such section.
70+
71+
72+
# Performance concerns
73+
74+
Most of the calculations are done serverside so it's important to have a good computer.
75+
76+
If the chart flickers and takes long time to load, deactivate updating the chart with each change of battery charge and grid consumption:
77+
```
78+
entities:
79+
- ${nordpool_sensor}
80+
# - ${battery_soc_sensor}
81+
# - ${grid_import_sensor}
82+
```
83+
84+
85+
--
86+
87+
Happy savings!

0 commit comments

Comments
 (0)