Add hourly electricity price chart

This commit is contained in:
2026-04-07 15:53:22 +02:00
parent b43e973c57
commit 457fbb2a04
2 changed files with 131 additions and 8 deletions
+2
View File
@@ -140,6 +140,8 @@ lovelace:
type: module type: module
- url: /hacsfiles/custom-gauge-card/custom-gauge-card.js - url: /hacsfiles/custom-gauge-card/custom-gauge-card.js
type: module type: module
- url: /local/community/apexcharts-card/apexcharts-card.js
type: module
dashboards: dashboards:
lovelace: lovelace:
mode: yaml mode: yaml
+123 -2
View File
@@ -295,10 +295,131 @@ cards:
red: 80 red: 80
# ⚡ Energi + 🍽️ Opvaskemaskine (kompakt) # ⚡ El-priser + 🍽️ Opvaskemaskine
- type: vertical-stack
cards:
- type: custom:apexcharts-card
graph_span: 24h
span:
start: hour
stacked: false
header:
show: true
title: El-priser næste 24 timer
show_states: true
colorize_states: true
now:
show: true
label: Nu
all_series_config:
stroke_width: 0
apex_config:
chart:
height: 260
grid:
strokeDashArray: 2
xaxis:
type: datetime
labels:
datetimeFormatter:
hour: HH:mm
yaxis:
decimalsInFloat: 2
tickAmount: 5
plotOptions:
bar:
columnWidth: 82%
borderRadius: 3
series:
- entity: sensor.energi_data_service
name: Pris
type: column
float_precision: 2
unit: ' kr/kWh'
show:
in_header: raw
in_chart: true
data_generator: |
const startOfHour = new Date();
startOfHour.setMinutes(0, 0, 0);
const endTime = startOfHour.getTime() + (24 * 60 * 60 * 1000);
const rawToday = entity.attributes.raw_today || [];
const rawTomorrow = entity.attributes.tomorrow_valid ? (entity.attributes.raw_tomorrow || []) : [];
const forecast = entity.attributes.forecast || [];
const allKnown = [...rawToday, ...rawTomorrow];
const data = [];
const seen = new Set();
const pushPoint = (item) => {
const timestamp = new Date(item.hour).getTime();
if (Number.isNaN(timestamp) || timestamp < startOfHour.getTime() || timestamp >= endTime || seen.has(timestamp)) {
return;
}
const price = Number(item.price);
if (Number.isNaN(price)) {
return;
}
seen.add(timestamp);
data.push({ x: timestamp, y: price });
};
allKnown.forEach(pushPoint);
if (data.length < 24) {
forecast.forEach(pushPoint);
}
data.sort((left, right) => left.x - right.x);
const trimmed = data.slice(0, 24);
if (!trimmed.length) {
return [];
}
const prices = trimmed.map((item) => item.y);
const minPrice = Math.min(...prices);
const maxPrice = Math.max(...prices);
const mix = (start, end, ratio) => Math.round(start + ((end - start) * ratio));
const toHex = (value) => value.toString(16).padStart(2, '0');
const rgbToHex = (red, green, blue) => `#${toHex(red)}${toHex(green)}${toHex(blue)}`;
const colorByValue = (value) => {
if (maxPrice === minPrice) {
return '#16a34a';
}
const normalized = (value - minPrice) / (maxPrice - minPrice);
if (normalized <= 0.5) {
const ratio = normalized / 0.5;
return rgbToHex(
mix(22, 250, ratio),
mix(163, 204, ratio),
mix(74, 21, ratio)
);
}
const ratio = (normalized - 0.5) / 0.5;
return rgbToHex(
mix(250, 220, ratio),
mix(204, 38, ratio),
mix(21, 38, ratio)
);
};
return trimmed.map((item) => ({
x: item.x,
y: item.y,
fillColor: colorByValue(item.y)
}));
- type: horizontal-stack - type: horizontal-stack
cards: cards:
- type: tile - type: tile
entity: sensor.dishwasher_next_start_compact entity: sensor.dishwasher_next_start_compact
name: Næste opvask name: Næste opvask