Det får du
Scriptene henter de åbne timepriser for dit postnummer og deler én kopi af dem på din Homey. Du sætter dem ind i HomeyScript og bruger dem fra dine Flows:
- Hent elprisen sætter tags med prisen i det kvarter, der kører nu, niveauet, kvarterets plads i døgnet, om morgendagens priser er ude, og dagens billigste time med prisen i den. Tags kan stå i alle dine Flows, fx i en besked.
- Billigste timer er en betingelse: Ja, når kvarteret, der kører nu, ligger i en af dagens billigste timer — hvor mange, skriver du selv på kortet.
- Percentil næste døgn er en betingelse til varmepumpen: Ja, når kvarteret ligger i det interval, du skriver, blandt de kendte kvarterer fra nu og et døgn frem.
- Køb nu eller vent er en betingelse til det, der skal være færdigt inden en frist: Ja i den billigste periode før fristen til opvaskemaskinen, eller i de billigste kvarterer før fristen til elbilen.
Hvorfor prisen for dit netområde, ikke spotprisen
Nord Pools spotpris for Vest- eller Østdanmark er en del af svaret, men kun en del: nettariffen, som dit netselskab tager for at bringe strømmen ud til dig, skifter trin hen over døgnet og er forskellig fra netselskab til netselskab; Energinets tariffer og elafgiften kommer oveni, og så momsen. Det kvarter, der er billigst på spotprisen, er ikke altid billigst, når tariffen regnes med — især ikke om vinteren, hvor tariffen er høj i spidsen om aftenen.
Scriptene bruger den fulde markedspris for dit netområde pr. kvarter: spotpris, nettarif, Energinets tariffer og elafgift, inkl. moms. Det er den pris, din elregning følger, hvis du har en variabel aftale — dit elselskabs eget tillæg og abonnement er ikke med, for de er ens hele døgnet og flytter ikke, hvornår strømmen er billig. Hvordan prisen regnes, står på metodesiden.
Filen, scriptene henter
Scriptene henter https://elpriser.dk/data/timepris.json?sted= efterfulgt af dit postnummer — de samme åbne timepriser, som guiden til Home Assistant bruger. Svaret har dagens timer og kvarterer og morgendagens, når de er udgivet. Hvert kvarter har sin start og slut i dansk tid, prisen i kroner pr. kWh, niveauet og pladsen i døgnet. Scriptene finder kvarteret, der kører nu, som det kvarter, hvis start og slut omslutter Homeys ur — de regner aldrig selv timer og minutter ud, så også nætterne, hvor uret stilles frem eller tilbage, går rigtigt. Alle felterne er beskrevet på siden med åbne data.
Sådan kommer du i gang
HomeyScript er Athoms egen app til små JavaScript-programmer på Homey. Den findes til Homey Pro, Homey Pro mini og Homey Self-Hosted Server — ikke til Homey Cloud — og kræver Homey-software 5.0.0 eller nyere.
- Installér HomeyScript fra Homey App Store på din Homey.
- Åbn HomeyScripts editor i Homeys webapp, my.homey.app, på en computer.
- Lav et nyt script, kald det Hent elprisen, og sæt koden herunder ind i stedet for det, der står.
- Skriv dit postnummer i stedet for
8000i linjenconst POSTNUMMER = '8000';øverst. Brug det samme postnummer i alle scriptene. - Gem scriptet, og kør det én gang fra editoren. Så opretter det sine tags, og loggen viser prisen for kvarteret, der kører nu.
- Lav et Flow, der kører scriptet hvert 15. minut: i Når-kolonnen et af Homeys indbyggede tidskort, der gentager Flowet med et fast interval, og i Så-kolonnen HomeyScripts kort Kør et script med Hent elprisen.
/*
* Hent elprisen — HomeyScript til Elpriser.dk's åbne timepriser.
* Vejledning: https://elpriser.dk/homey/
*
* Sætter tags med elprisen for dit postnummer i det kvarter, der kører nu — alt inkl. moms
* (spotpris, nettarif, Energinets tariffer og elafgift), uden dit elselskabs tillæg og abonnement.
* Lad et Flow køre scriptet hvert 15. minut.
*
* Data: https://elpriser.dk/data/timepris.json, åbne data under CC BY 4.0.
* Viser du tallene for andre, så skriv: "Kilde: Elpriser.dk, https://elpriser.dk/data/#timepris"
*/
// Dit postnummer. Dækker det flere netområder, så skriv a og netområdets kode, fx 'a151'.
const POSTNUMMER = '8000';
const nu = Date.now();
const kopi = await hentPriser((f, gemt) => nu - gemt.hentet < 5 * 60 * 1000);
const fundet = findKvarter(kopi, nu);
if (!fundet) {
// Ingen pris for kvarteret, der kører nu. Tallene bliver stående fra sidst, men tags siger,
// at de ikke er aktuelle — og betingelseskortene svarer Nej, til der er priser igen.
await tag('Elpris aktuel', false);
await tag('Elpris niveau', 'ukendt');
await tag('Elpris kvarter', 'ukendt');
log('Ingen pris for kvarteret, der kører nu.');
return false;
}
const k = fundet.kvarter;
await tag('Elpris nu', k.kr); // kr./kWh inkl. moms
await tag('Elpris niveau', k.niveau); // billig, middel eller dyr
await tag('Elpris percentil', k.percentil); // 0 (billigst) til 100 (dyrest) i døgnet
await tag('Elpris placering', k.placering); // 1 er døgnets billigste kvarter
await tag('Elpris kvarter', k.tekst); // fx 21:45–22:00
await tag('Elpris i morgen klar', fundet.senereDag);
if (fundet.dag.billigsteTime) {
await tag('Elpris billigste time', fundet.dag.billigsteTime.time); // fx 4 for kl. 04–05
await tag('Elpris billigste time pris', fundet.dag.billigsteTime.kr);
}
await tag('Elpris hentet', klokkeslaet(kopi)); // den sidste gode hentning, dansk tid
await tag('Elpris aktuel', true);
log(k.tekst + ': ' + k.kr + ' kr./kWh inkl. moms, ' + k.niveau);
return true;
// Klokkeslættet for den sidste gode hentning i dansk tid, regnet med offset fra filens eget kvarter.
function klokkeslaet(gemt) {
const f = findKvarter(gemt, gemt.hentet);
const offset = f ? f.kvarter.offset : gemt.dage[0].kvarterer[0].offset;
const minutter = (offset.charAt(0) === '-' ? -1 : 1) * (Number(offset.slice(1, 3)) * 60 + Number(offset.slice(4, 6)));
return new Date(gemt.hentet + minutter * 60 * 1000).toISOString().slice(11, 16);
}
// ── Fælles for scriptene til Elpriser.dk: ens i dem alle ──────────────────────
// Priserne for i dag og i morgen (når de er udgivet). Scriptene deler én kopi af filen på din
// Homey og henter kun en ny, når kopien ikke er god nok — og højst hvert femte minut.
async function hentPriser(godNok) {
const navn = 'elpriser-' + POSTNUMMER;
const nu = Date.now();
let kopi = global.get(navn);
if (!kopi || kopi.version !== 1) kopi = null;
const fundet = findKvarter(kopi, nu);
if (fundet && godNok(fundet, kopi)) return kopi;
if (kopi && nu - kopi.hentet < 5 * 60 * 1000) return kopi;
try {
// timeout: giv op efter ti sekunder — HomeyScript stopper selv et script efter et halvt minut.
const svar = await fetch('https://elpriser.dk/data/timepris.json?sted=' + encodeURIComponent(POSTNUMMER), {
headers: { 'user-agent': 'elpriser-homeyscript/1' },
timeout: 10000,
});
if (!svar.ok) throw new Error('filen svarede ' + svar.status);
kopi = kompakt(await svar.json(), nu);
global.set(navn, kopi);
} catch (fejl) {
log('Kunne ikke hente elpriserne: ' + fejl.message);
}
return kopi;
}
// Kvarteret, der kører på tidspunktet: det, hvis egen start og slut i filen omslutter det.
// Homeys ur regnes aldrig om til timer og minutter, så natten, hvor uret stilles, går også rigtigt.
function findKvarter(kopi, tid) {
if (!kopi) return null;
for (let d = 0; d < kopi.dage.length; d++) {
const kvarterer = kopi.dage[d].kvarterer;
for (let i = 0; i < kvarterer.length; i++) {
if (kvarterer[i].fra <= tid && tid < kvarterer[i].til) {
return { dag: kopi.dage[d], kvarter: kvarterer[i], senereDag: d < kopi.dage.length - 1 };
}
}
}
return null;
}
// Det, scriptene bruger af filen: dagene med deres timer og kvarterer.
function kompakt(fil, hentet) {
if (!fil || !fil.today || !Array.isArray(fil.today.quarters)) throw new Error('filen havde en uventet form');
const dage = [fil.today, fil.tomorrow].filter(Boolean).map((dag) => ({
dag: dag.day,
billigsteTime: dag.cheapestHour ? { time: dag.cheapestHour.hour, kr: dag.cheapestHour.krPerKwhInclVat } : null,
timer: dag.hours.map((t) => ({ time: t.hour, placering: t.rank })),
kvarterer: dag.quarters.map((q) => ({
fra: Date.parse(q.start),
til: Date.parse(q.end),
time: q.hour,
kr: q.krPerKwhInclVat,
oere: q.oreInclVat,
niveau: q.levelWord,
percentil: q.percentile,
placering: q.rank,
tekst: q.start.slice(11, 16) + '–' + q.end.slice(11, 16),
offset: q.start.slice(-6),
})),
}));
return { version: 1, hentet: hentet, dage: dage };
}
// ── Slut på det fælles ─────────────────────────────────────────────────────────
Tags, scriptet sætter
- Elpris nu: prisen i det kvarter, der kører, i kr./kWh inkl. moms.
- Elpris niveau: billig, middel eller dyr efter døgnets egne tredjedele.
- Elpris percentil: kvarterets plads blandt døgnets kvarterer fra 0 (det billigste) til 100 (det dyreste); lige priser får samme tal.
- Elpris placering: 1 er døgnets billigste kvarter.
- Elpris kvarter: det kvarter, tallene hører til, fx 21:45–22:00.
- Elpris i morgen klar: Ja, når morgendagens priser er ude.
- Elpris billigste time og Elpris billigste time pris: dagens billigste time som timetal — 4 betyder kl. 04–05 — og prisen i den.
- Elpris hentet: klokkeslættet for den sidste gode hentning.
- Elpris aktuel: Ja, når scriptet havde en pris for kvarteret, da det sidst kørte.
Tags bruges som Homeys andre tags: sæt dem ind i et kort, fx en besked med dagens billigste time og prisen i den. De skifter, når Flowet kører. Falder kørslerne ikke på kvarterets start, står det forrige kvarters tal lidt ind i det næste — Elpris kvarter viser, hvilket kvarter tallene hører til. Betingelseskortene nedenfor regner selv med det kvarter, der kører, når de spørges.
Når der ingen pris er
Kan scriptet ikke hente filen, bruger det kopien fra sidste gang, så længe den har en pris for kvarteret, der kører — dagens priser ligger fast, når de er udgivet. Har kopien ingen pris for kvarteret, sætter scriptet Elpris aktuel til Nej og Elpris niveau og Elpris kvarter til »ukendt«, og betingelseskortene svarer Nej, til der er priser igen. Tallene bliver stående fra sidst, så dine Flows bliver ved med at kende tags — bruger et Flow tallene i en betingelse, så lad det også se på Elpris aktuel.
Et Flow: tænd, når strømmen er billig
Det klassiske eksempel: varmtvandsbeholderen skal varme i dagens billigste timer. Lav et script mere, Billigste timer, med koden herunder og dit postnummer øverst. Det er en betingelse, der svarer Ja, når kvarteret, der kører nu, ligger i en af dagens N billigste timer, og Nej ellers — også når der ingen priser er. N er argumentet, du skriver på kortet. Betingelsesscriptene køres fra kortet med deres argument; i editoren, uden argument, svarer de med en fejl, der siger, hvad der skal stå.
/*
* Billigste timer — betingelse til HomeyScript med Elpriser.dk's åbne timepriser.
* Vejledning: https://elpriser.dk/homey/
*
* Til kortet »Kør et script med et argument« i Og-kolonnen. Argumentet er et antal timer, fx 4:
* kortet svarer Ja, når kvarteret, der kører nu, ligger i en af dagens 4 billigste timer for dit
* postnummer (alt inkl. moms), og Nej ellers — også når der ingen priser er.
*
* Data: https://elpriser.dk/data/timepris.json, åbne data under CC BY 4.0.
* Viser du tallene for andre, så skriv: "Kilde: Elpriser.dk, https://elpriser.dk/data/#timepris"
*/
// Dit postnummer. Dækker det flere netområder, så skriv a og netområdets kode, fx 'a151'.
const POSTNUMMER = '8000';
const antal = Number(String(args[0] || '').trim());
if (!Number.isInteger(antal) || antal < 1 || antal > 24) {
throw new Error('Skriv et antal timer fra 1 til 24 som argument, fx 4.');
}
const nu = Date.now();
const fundet = findKvarter(await hentPriser(() => true), nu);
if (!fundet) {
log('Ingen pris for kvarteret, der kører nu — svarer Nej.');
return false;
}
// Timen, kvarteret hører til, og dens plads blandt dagens timer: 1 er den billigste, og lige
// priser deler pladsen. Natten, hvor uret stilles tilbage, er den dobbelte time 02 én time i filen.
const time = fundet.dag.timer.find((t) => t.time === fundet.kvarter.time);
const ja = !!time && time.placering <= antal;
log(fundet.kvarter.tekst + ': timen har plads ' + (time ? time.placering : '–') + ' blandt dagens timer — ' + (ja ? 'Ja' : 'Nej'));
return ja;
// ── Fælles for scriptene til Elpriser.dk: ens i dem alle ──────────────────────
// Priserne for i dag og i morgen (når de er udgivet). Scriptene deler én kopi af filen på din
// Homey og henter kun en ny, når kopien ikke er god nok — og højst hvert femte minut.
async function hentPriser(godNok) {
const navn = 'elpriser-' + POSTNUMMER;
const nu = Date.now();
let kopi = global.get(navn);
if (!kopi || kopi.version !== 1) kopi = null;
const fundet = findKvarter(kopi, nu);
if (fundet && godNok(fundet, kopi)) return kopi;
if (kopi && nu - kopi.hentet < 5 * 60 * 1000) return kopi;
try {
// timeout: giv op efter ti sekunder — HomeyScript stopper selv et script efter et halvt minut.
const svar = await fetch('https://elpriser.dk/data/timepris.json?sted=' + encodeURIComponent(POSTNUMMER), {
headers: { 'user-agent': 'elpriser-homeyscript/1' },
timeout: 10000,
});
if (!svar.ok) throw new Error('filen svarede ' + svar.status);
kopi = kompakt(await svar.json(), nu);
global.set(navn, kopi);
} catch (fejl) {
log('Kunne ikke hente elpriserne: ' + fejl.message);
}
return kopi;
}
// Kvarteret, der kører på tidspunktet: det, hvis egen start og slut i filen omslutter det.
// Homeys ur regnes aldrig om til timer og minutter, så natten, hvor uret stilles, går også rigtigt.
function findKvarter(kopi, tid) {
if (!kopi) return null;
for (let d = 0; d < kopi.dage.length; d++) {
const kvarterer = kopi.dage[d].kvarterer;
for (let i = 0; i < kvarterer.length; i++) {
if (kvarterer[i].fra <= tid && tid < kvarterer[i].til) {
return { dag: kopi.dage[d], kvarter: kvarterer[i], senereDag: d < kopi.dage.length - 1 };
}
}
}
return null;
}
// Det, scriptene bruger af filen: dagene med deres timer og kvarterer.
function kompakt(fil, hentet) {
if (!fil || !fil.today || !Array.isArray(fil.today.quarters)) throw new Error('filen havde en uventet form');
const dage = [fil.today, fil.tomorrow].filter(Boolean).map((dag) => ({
dag: dag.day,
billigsteTime: dag.cheapestHour ? { time: dag.cheapestHour.hour, kr: dag.cheapestHour.krPerKwhInclVat } : null,
timer: dag.hours.map((t) => ({ time: t.hour, placering: t.rank })),
kvarterer: dag.quarters.map((q) => ({
fra: Date.parse(q.start),
til: Date.parse(q.end),
time: q.hour,
kr: q.krPerKwhInclVat,
oere: q.oreInclVat,
niveau: q.levelWord,
percentil: q.percentile,
placering: q.rank,
tekst: q.start.slice(11, 16) + '–' + q.end.slice(11, 16),
offset: q.start.slice(-6),
})),
}));
return { version: 1, hentet: hentet, dage: dage };
}
// ── Slut på det fælles ─────────────────────────────────────────────────────────
Flowet i eksemplet:
- Når: tidskortet, hvert 15. minut.
- Og: HomeyScripts kort Kør et script med et argument med Billigste timer og argumentet
4. - Så: tænd varmtvandsbeholderens stik.
- Når betingelsen ikke er opfyldt: sluk stikket — i gruppen under Så-kolonnen, som Athom kalder Else.
Dagens timer har hver sin plads, hvor 1 er den billigste, og lige priser deler pladsen. Natten, hvor uret stilles tilbage, er den dobbelte time 02 én time i filen, så kortet svarer det samme i begge.
Varmepumpen: flere trin med percentilen
En varmepumpe skal ikke tænde og slukke; den skal flytte varmen lidt hen over døgnet. Huset er et lager: et par grader ekstra i de billige kvarterer holder i timevis, og en grad mindre i de dyre mærkes knap. Til det er tre niveauer grove, og derfor står percentilen i filen — kvarterets plads fra 0 (det billigste) til 100 (det dyreste), så et Flow kan have de trin, du vil.
Tagget Elpris percentil starter forfra ved midnat, så et kvarter kl. 23.45 kan hedde billigt, selv om nattens priser er endnu lavere. Scriptet Percentil næste døgn placerer i stedet kvarteret blandt de kendte kvarterer fra nu og et døgn frem, med morgendagen, når den er udgivet (typisk mellem kl. 13 og 15 — tallet kan flytte sig dér). Det er en betingelse med et interval som argument, fx 0-20, og svarer Ja, når kvarterets plads ligger i intervallet; grænserne er med. Kender filen færre end fire timer frem, svarer det Nej.
/*
* Percentil næste døgn — betingelse til HomeyScript med Elpriser.dk's åbne timepriser.
* Vejledning: https://elpriser.dk/homey/
*
* Til kortet »Kør et script med et argument« i Og-kolonnen. Argumentet er et interval, fx 0-20:
* kortet svarer Ja, når kvarteret, der kører nu, ligger i det interval blandt de kendte kvarterer
* fra nu og et døgn frem — 0 er det billigste, 100 det dyreste. Til varmepumpen. Nej, når færre
* end fire timer frem er kendt, eller der ingen priser er.
*
* Data: https://elpriser.dk/data/timepris.json, åbne data under CC BY 4.0.
* Viser du tallene for andre, så skriv: "Kilde: Elpriser.dk, https://elpriser.dk/data/#timepris"
*/
// Dit postnummer. Dækker det flere netområder, så skriv a og netområdets kode, fx 'a151'.
const POSTNUMMER = '8000';
const interval = /^\s*(\d{1,3})\s*[-–]\s*(\d{1,3})\s*$/.exec(String(args[0] || ''));
const fra = interval ? Number(interval[1]) : -1;
const til = interval ? Number(interval[2]) : -1;
if (!interval || fra > til || til > 100) {
throw new Error('Skriv et interval fra 0 til 100 som argument, fx 0-20.');
}
const nu = Date.now();
// Morgendagens priser skal med, så snart de er ude: mangler de, hentes filen igen (højst hvert femte minut).
const p = percentilNaesteDoegn(await hentPriser((f) => f.senereDag), nu);
if (p === null) {
log('Percentilen kendes ikke lige nu — svarer Nej.');
return false;
}
const ja = fra <= p && p <= til;
log('Percentil næste døgn: ' + p + ' — ' + (ja ? 'Ja' : 'Nej') + ' for ' + fra + '-' + til);
return ja;
// Kvarterets plads blandt de kendte kvarterer fra nu og et døgn frem, som filens now.ahead:
// 100 · (antal lavere + (antal lige - 1) / 2) / (antal kvarterer - 1). Null under fire timer.
function percentilNaesteDoegn(kopi, tid) {
if (!kopi) return null;
const alle = kopi.dage.reduce((liste, dag) => liste.concat(dag.kvarterer), []);
const i = alle.findIndex((k) => k.fra <= tid && tid < k.til);
if (i < 0) return null;
const vindue = alle.slice(i).filter((k) => k.fra < alle[i].fra + 24 * 60 * 60 * 1000);
if (vindue.length < 16) return null;
const pris = alle[i].oere;
const lavere = vindue.filter((k) => k.oere < pris).length;
const lige = vindue.filter((k) => k.oere === pris).length;
return Math.round((100 * (lavere + (lige - 1) / 2)) / (vindue.length - 1));
}
// ── Fælles for scriptene til Elpriser.dk: ens i dem alle ──────────────────────
// Priserne for i dag og i morgen (når de er udgivet). Scriptene deler én kopi af filen på din
// Homey og henter kun en ny, når kopien ikke er god nok — og højst hvert femte minut.
async function hentPriser(godNok) {
const navn = 'elpriser-' + POSTNUMMER;
const nu = Date.now();
let kopi = global.get(navn);
if (!kopi || kopi.version !== 1) kopi = null;
const fundet = findKvarter(kopi, nu);
if (fundet && godNok(fundet, kopi)) return kopi;
if (kopi && nu - kopi.hentet < 5 * 60 * 1000) return kopi;
try {
// timeout: giv op efter ti sekunder — HomeyScript stopper selv et script efter et halvt minut.
const svar = await fetch('https://elpriser.dk/data/timepris.json?sted=' + encodeURIComponent(POSTNUMMER), {
headers: { 'user-agent': 'elpriser-homeyscript/1' },
timeout: 10000,
});
if (!svar.ok) throw new Error('filen svarede ' + svar.status);
kopi = kompakt(await svar.json(), nu);
global.set(navn, kopi);
} catch (fejl) {
log('Kunne ikke hente elpriserne: ' + fejl.message);
}
return kopi;
}
// Kvarteret, der kører på tidspunktet: det, hvis egen start og slut i filen omslutter det.
// Homeys ur regnes aldrig om til timer og minutter, så natten, hvor uret stilles, går også rigtigt.
function findKvarter(kopi, tid) {
if (!kopi) return null;
for (let d = 0; d < kopi.dage.length; d++) {
const kvarterer = kopi.dage[d].kvarterer;
for (let i = 0; i < kvarterer.length; i++) {
if (kvarterer[i].fra <= tid && tid < kvarterer[i].til) {
return { dag: kopi.dage[d], kvarter: kvarterer[i], senereDag: d < kopi.dage.length - 1 };
}
}
}
return null;
}
// Det, scriptene bruger af filen: dagene med deres timer og kvarterer.
function kompakt(fil, hentet) {
if (!fil || !fil.today || !Array.isArray(fil.today.quarters)) throw new Error('filen havde en uventet form');
const dage = [fil.today, fil.tomorrow].filter(Boolean).map((dag) => ({
dag: dag.day,
billigsteTime: dag.cheapestHour ? { time: dag.cheapestHour.hour, kr: dag.cheapestHour.krPerKwhInclVat } : null,
timer: dag.hours.map((t) => ({ time: t.hour, placering: t.rank })),
kvarterer: dag.quarters.map((q) => ({
fra: Date.parse(q.start),
til: Date.parse(q.end),
time: q.hour,
kr: q.krPerKwhInclVat,
oere: q.oreInclVat,
niveau: q.levelWord,
percentil: q.percentile,
placering: q.rank,
tekst: q.start.slice(11, 16) + '–' + q.end.slice(11, 16),
offset: q.start.slice(-6),
})),
}));
return { version: 1, hentet: hentet, dage: dage };
}
// ── Slut på det fælles ─────────────────────────────────────────────────────────
Eksemplet er tre Flows, der alle kører hvert 15. minut og har HomeyScripts kort Kør et script med et argument med Percentil næste døgn i Og-kolonnen:
- Med argumentet
0-20: sæt varmepumpen til 22,5 grader — lidt varmere i den billigste femtedel. - Med argumentet
21-79: sæt den til 21 grader — normalen i midten. - Med argumentet
80-100: sæt den til 20 grader — lidt køligere i den dyreste femtedel.
Graderne er et eksempel; brug din varmepumpes egne kort og dine egne temperaturer.
Køb nu eller vent: en frist og et antal timer
For opvaskemaskinen og elbilen er spørgsmålet det samme: de skal bruge strøm i et antal timer, og det skal være færdigt inden et bestemt tidspunkt — skal de starte nu eller vente? Scriptet Køb nu eller vent spørger filen om planen (timer og inden på adressen) og svarer Ja eller Nej. Argumentet er et antal timer og en frist, fx 3 07:00: Ja, når kvarteret, der kører nu, ligger i den billigste periode på tre timer i træk, der kan nå at blive færdig inden kl. 7. Skriv fleksibel bagefter, fx 4 07:00 fleksibel, til forbrug, der kan afbrydes: så svarer kortet Ja i de billigste kvarterer før fristen, også når de ikke ligger i træk.
Kan forbruget ikke nå at blive færdigt før fristen, hvis det venter, svarer kortet Ja — så er svaret at starte nu. Ligger fristen efter de kendte priser, svarer det Nej, til de næste priser er ude; morgendagens kommer typisk mellem kl. 13 og 15. Fristen er næste gang, uret viser klokkeslættet, så den ligger fast.
/*
* Køb nu eller vent — betingelse til HomeyScript med Elpriser.dk's åbne timepriser.
* Vejledning: https://elpriser.dk/homey/
*
* Til kortet »Kør et script med et argument« i Og-kolonnen. Argumentet er et antal timer og en
* frist, fx 3 07:00: kortet svarer Ja, når kvarteret, der kører nu, ligger i den billigste periode
* på 3 timer i træk, der kan nå at blive færdig inden kl. 7 — til opvaskemaskinen. Skriv fleksibel
* bagefter, fx 4 07:00 fleksibel, til forbrug, der kan afbrydes, som elbilen: så svarer kortet Ja
* i de billigste kvarterer før fristen, også når de ikke ligger i træk.
*
* Data: https://elpriser.dk/data/timepris.json, åbne data under CC BY 4.0.
* Viser du tallene for andre, så skriv: "Kilde: Elpriser.dk, https://elpriser.dk/data/#timepris"
*/
// Dit postnummer. Dækker det flere netområder, så skriv a og netområdets kode, fx 'a151'.
const POSTNUMMER = '8000';
const dele = String(args[0] || '').trim().split(/\s+/);
const fleksibel = dele.length === 3 && dele[2].toLowerCase() === 'fleksibel';
if (dele.length !== 2 && !fleksibel) {
throw new Error('Skriv timer og frist som argument, fx 3 07:00 eller 4 07:00 fleksibel.');
}
const nu = Date.now();
const plan = await hentPlan(dele[0], dele[1]);
if (!plan) {
log('Ingen plan lige nu — svarer Nej.');
return false;
}
if (!plan.fits) {
// Der er ikke tid til at vente: filens svar er at starte nu.
log('For lidt tid til at vente før fristen — svarer Ja.');
return true;
}
if (!plan.complete) {
// Fristen ligger efter de kendte priser: vent på de næste (morgendagens kommer typisk mellem kl. 13 og 15).
log('Fristen ligger efter de kendte priser — svarer Nej, til de næste er ude.');
return false;
}
const iKvarteret = (start) => Date.parse(start) <= nu && nu < Date.parse(start) + 15 * 60 * 1000;
const ja = fleksibel
? !!plan.flexible && plan.flexible.starts.some(iKvarteret)
: !!plan.block && Date.parse(plan.block.start) <= nu && nu < Date.parse(plan.block.end);
log((fleksibel ? 'De billigste kvarterer' : 'Den billigste periode') + ' før fristen — ' + (ja ? 'Ja' : 'Nej'));
return ja;
// Planen fra filen (?timer=&inden=). Svaret gemmes i fem minutter, så kortet højst spørger hvert femte minut.
async function hentPlan(timer, inden) {
const navn = 'elpriser-plan-' + POSTNUMMER + '-' + timer + '-' + inden;
const gemt = global.get(navn);
if (gemt && Date.now() - gemt.hentet < 5 * 60 * 1000) return gemt.plan;
let svar;
try {
// timeout: giv op efter ti sekunder — HomeyScript stopper selv et script efter et halvt minut.
svar = await fetch(
'https://elpriser.dk/data/timepris.json?sted=' + encodeURIComponent(POSTNUMMER) +
'&timer=' + encodeURIComponent(timer) + '&inden=' + encodeURIComponent(inden),
{ headers: { 'user-agent': 'elpriser-homeyscript/1' }, timeout: 10000 },
);
} catch (fejl) {
log('Kunne ikke hente planen: ' + fejl.message);
return null;
}
if (svar.status === 400) {
// Et argument, filen ikke kan bruge: kortet fejler med filens egen forklaring.
const fejl = await svar.json().catch(() => ({}));
throw new Error(fejl.error || 'Argumentet kunne ikke bruges.');
}
if (!svar.ok) {
log('Kunne ikke hente planen: filen svarede ' + svar.status);
return null;
}
try {
const plan = (await svar.json()).plan || null;
global.set(navn, { hentet: Date.now(), plan: plan });
return plan;
} catch (fejl) {
log('Kunne ikke læse planen: ' + fejl.message);
return null;
}
}
Opvaskemaskinen
Opvaskemaskinen skal starte én gang, når den er fyldt. Lav en Ja/Nej-variabel, Opvask klar, under Logik, og sæt den til Ja, når maskinen er fyldt, fx fra en knap eller et andet Flow. Flowet:
- Når: tidskortet, hvert 15. minut.
- Og: et kort fra Logik, der tjekker, at Opvask klar er Ja, og HomeyScripts kort Kør et script med et argument med Køb nu eller vent og argumentet
3 07:00. - Så: start opvaskemaskinen, og Indstil Opvask klar til Nej under Logik.
Planen regnes forfra, hver gang kortet spørger, så det er variablen, der sørger for, at maskinen kun starter én gang.
Elbilen
Elbilen kan lade i stykker og bruger derfor fleksibel. Flowet:
- Når: tidskortet, hvert 15. minut.
- Og: Køb nu eller vent med argumentet
4 07:00 fleksibel. - Så: start opladningen. Når betingelsen ikke er opfyldt (Else): stop den.
Kortet ved ikke, hvor meget bilen allerede har ladet. Det regner hver gang med de timer, du skrev, fra nu til fristen, og når der kun er de timer tilbage, svarer det Ja hele vejen, så bilen når at lade inden fristen. Bilen stopper selv, når den er fuld. Brug din ladeboks' eller bilens egne kort til at starte og stoppe.
Regler for brug
- Filen er åbne data under Creative Commons BY 4.0: fri brug, også kommercielt, mod kildeangivelsen »Kilde: Elpriser.dk«. Den står også øverst i scriptene.
- Hent hver adresse højst hvert femte minut pr. installation — scriptene gør det af sig selv. Dagens priser ligger fast; morgendagens kommer typisk mellem kl. 13 og 15.
- Prisen er markedets pris alt inkl. moms uden dit elselskabs tillæg og abonnement. Vil du have dem med, så læg dem til i dit Flow.
- Vi kan ændre filens form med varsel på siden med åbne data; felter fjernes ikke uden varsel.
På vej: en Homey-app
Scriptene virker i dag og bliver ved med det. Næste skridt er en app til Homey fra Elpriser.dk med de samme tal, uden scripts. Apps til Homey gennemgås af Athom, før de kan installeres fra Homey App Store, så vi sætter ingen dato på. Når appen kan installeres, står det her på siden.


