1. Forside
  2. Scripts
  3. Aktuelle elpriser

Scripts

Aktuelle elpriser

Hent den rigtige danske elpris ind på enheden, vis den i appen, og styr en udgang efter den.

Opdateret september 2026

Sådan virker det

Enheden henter selv prisen over internettet direkte fra stromligning.dk — der er ingen server eller tjeneste imellem, som du skal holde kørende. Enheden skal have internetadgang, og skal prisen vises i appen, sender scriptet den videre til din Shelly-konto.

Prisen derfra er den, kunden betaler: spotprisen plus netselskabets tariffer og afgifter, fundet ud fra koordinaterne. Den hentes én gang i timen.

Skal du gøre det samme i Sverige eller Finland, står kilderne på den engelske udgave af denne vejledning, Live electricity prices (på engelsk).

Det skal du have

  • En Shelly-enhed, der understøtter scripts — Gen2 eller nyere
  • Mindst én enhed med effektmåling (PM eller EM), hvis forbruget også skal måles
  • Shelly Smart Control eller control.shelly.cloud. En computer er nemmere end en telefon til scripts
  • Gen3 eller nyere, hvis prisen skal vises som en enhed i appen — det kræver virtuelle komponenter
1
Script

Indsæt scriptet

Tryk på enheden, vælg script-ikonet, og tryk Opret nyt script. Giv scriptet et navn, sæt koden nedenfor ind, og tryk Gem. Det kan også gøres fra enhedens eget webinterface.

Lad editoren stå åben. I de næste to trin finder du de oplysninger, der skal skrives ind øverst i scriptet.

Script-oversigten på enheden med knappen Opret nyt script, og editoren nedenunder med navnefeltet og knapperne til at gemme og lukke
Tryk for at forstørre.
// Elpris fra stromligning.dk — spotpris, netselskabets tariffer og afgifter
// ===== KONFIGURATION =====
let CONFIG = {
  lat: "55.6736",            // Breddegrad (trin 2)
  long: "12.5681",           // Længdegrad (trin 2)
  additional_cost: 0.00,     // Ekstra omkostninger fra dit elselskab, kr/kWh
  // Hele URL'en kopieres fra appen (trin 3) og erstatter eksemplet herunder —
  // gåseøjnene skal blive stående. Så længe eksemplet står der, sendes prisen
  // ikke til appen. Det samme gælder et helt tomt felt ("")
  post_url: "https://shelly-XX-eu.shelly.cloud/v2/user/pp-ltu/DIN-TOKEN-HER",
  vc_id: 200,                // Virtuel Number-komponent. 0 = brug den ikke
  control_enabled: false,    // true = styr en udgang efter prisen
  switch_id: 0,              // Hvilken udgang der styres
  price_limit: 1.50,         // Tænd under denne pris, kr/kWh
  check_every_minute: false, // true henter prisen hvert minut. Kun til test — det belaster serveren, priserne hentes fra
  check_delay_seconds: 10,   // Sekunder efter time- eller minutskiftet, hvor prisen hentes
};
// ===== KONFIGURATION SLUT =====

let current_price = null;
let current_supplier = null;
let current_hour = 0;

// Shellys scriptmotor understøtter ikke regulære udtryk, så kolon erstattes manuelt
function encodeColon(str) {
  return str.split(":").join("%3A");
}

function pad2(n) {
  return ("0" + n).slice(-2);
}

// Eksemplet i post_url er en pladsholder og ikke en adresse, der svarer. Står
// det der endnu — eller er feltet tomt — springes afsendelsen over, så en
// uopsat enhed ikke forsøger en forgæves POST i timen
function postUrlIsSet() {
  return CONFIG.post_url !== "" && CONFIG.post_url.indexOf("DIN-TOKEN-HER") === -1;
}

// Slår afsendelsen fejl, skal resten af scriptet køre videre uanset hvad:
// prisen er hentet, udgangen er styret, og kun visningen i appen mangler
function postResult(res, code, msg) {
  if (code !== 0) {
    print("Kunne ikke sende prisen til appen — tjek post_url");
  } else {
    print("Pris sendt til appen: " + current_price);
  }
}

function handlePrice(price) {
  current_price = price;
  print("Elpris nu: " + price + " kr/kWh");

  if (CONFIG.vc_id) {
    // Number.Set kræver et tal. Sender man teksten fra toFixed(2), bliver
    // komponenten aldrig opdateret — lav det ikke om til tekst igen
    Shelly.call("Number.Set", { id: CONFIG.vc_id, value: price });
  }

  if (CONFIG.control_enabled) {
    let cheap = price < CONFIG.price_limit;
    print("Graense " + CONFIG.price_limit + " — udgang " + (cheap ? "TAENDT" : "SLUKKET"));
    Shelly.call("Switch.Set", { id: CONFIG.switch_id, on: cheap });
  }

  if (postUrlIsSet()) {
    // Appen har modtaget prisen som tekst med to decimaler siden 2025.
    // Her skal den altså være tekst — modsat Number.Set ovenfor
    Shelly.call("HTTP.Request", {
      method: "POST",
      url: CONFIG.post_url,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ price: price.toFixed(2) }),
      timeout: 5,
    }, postResult);
  } else {
    // Ikke en fejl. At lade være med at vise prisen i appen er en gyldig opsætning
    print("Prisen sendes ikke til appen — post_url er ikke udfyldt.");
  }
}

function onPrices(res, code, msg) {
  if (code !== 0) {
    print("Fejl ved hentning af pris: " + msg);
    scheduleNextCheck();
    return;
  }
  let data = JSON.parse(res.body);
  if (!data.prices || data.prices.length === 0) {
    print("Ingen prisdata modtaget.");
    scheduleNextCheck();
    return;
  }
  let found = false;
  for (let i = 0; i < data.prices.length; i++) {
    let item = data.prices[i];
    if (new Date(item.date).getHours() === current_hour) {
      found = true;
      let total = item.price.total + CONFIG.additional_cost;
      handlePrice(Math.floor(total * 100 + 0.5) / 100);
    }
  }
  if (!found) {
    print("Ingen pris fundet for timen " + current_hour + ":00.");
  }
  scheduleNextCheck();
}

function onSupplier(res, code, msg) {
  if (code !== 0) {
    print("Fejl ved opslag af netselskab: " + msg);
    scheduleNextCheck();
    return;
  }
  let suppliers = JSON.parse(res.body);
  if (!suppliers || suppliers.length === 0) {
    print("Intet netselskab fundet for de angivne koordinater.");
    scheduleNextCheck();
    return;
  }
  current_supplier = suppliers[0];
  print("Netselskab: " + current_supplier.id + " — prisomraade: " + current_supplier.priceArea);

  let now = new Date();
  current_hour = now.getHours();
  let stamp = now.getFullYear() + "-" + pad2(now.getMonth() + 1) + "-" + pad2(now.getDate()) + "T" + pad2(current_hour);
  let from = encodeColon(stamp + ":00:00");
  let to = encodeColon(stamp + ":45:00");
  let priceUrl = "https://stromligning.dk/api/prices?from=" + from + "&to=" + to +
    "&supplierId=" + current_supplier.id + "&aggregation=1h&priceArea=" + current_supplier.priceArea;

  Shelly.call("HTTP.GET", { url: priceUrl }, onPrices);
}

function getCurrentPrice() {
  let supplierUrl = "https://stromligning.dk/api/suppliers/find?lat=" + CONFIG.lat + "&long=" + CONFIG.long;
  Shelly.call("HTTP.GET", { url: supplierUrl }, onSupplier);
}

function scheduleNextCheck() {
  let now = new Date();
  let delay;
  if (CONFIG.check_every_minute) {
    let msIntoMinute = now.getSeconds() * 1000;
    delay = (60 * 1000) - msIntoMinute + (CONFIG.check_delay_seconds * 1000);
  } else {
    let msIntoHour = (now.getMinutes() * 60 + now.getSeconds()) * 1000;
    delay = (3600 * 1000) - msIntoHour + (CONFIG.check_delay_seconds * 1000);
  }
  print("Naeste prischeck om " + Math.floor(delay / 1000) + " sekunder");
  Timer.set(delay, false, getCurrentPrice);
}

getCurrentPrice();
2
Placering

Find dine koordinater og sæt dem ind

Gå i appen til Indstillinger (tandhjulet) → Geografisk placering og tidszoneVælg fra kort. Træk markøren hen i nærheden af adressen, og tryk Vælg.

Skriv breddegraden i lat og længdegraden i long øverst i scriptet. Begge tal står i gåseøjne, og det skal de blive ved med.

Det er nok, at markøren ligger i området. Kun hvis kunden bor lige på grænsen mellem to netselskaber, skal den være præcis.

Enhedens indstillinger med breddegrad og længdegrad, og dialogen Vælg fra kort, hvor en markør trækkes hen på adressen
Tryk for at forstørre.
3
Token

Find din token og sæt den ind

Gå til EnergiElpris, tryk på Live, og rul ned til API URL. Kopiér hele URL’en, og sæt den ind i post_url i stedet for eksemplet. Gåseøjnene om URL’en skal blive stående.

Erstat hele URL’en frem for at rette i eksemplet: tallet efter shelly- er forskelligt fra konto til konto, så den del skal også med fra appen.

Så længe eksemplet står i feltet, sender scriptet ikke prisen nogen steder. Det skriver en linje i konsollen om det og kører videre, så en enhed, der ikke er sat helt op, ikke banker på en adresse, der ikke findes. Skal prisen aldrig vises i appen, kan du lade eksemplet stå eller sætte feltet til to gåseøjne uden noget imellem, "".

Fanen Elpris under Energi med knapperne Manuelt og Live, og feltet API URL nederst med den tokeniserede adresse og en kopiknap
Tryk for at forstørre.
4
Opsætning

Tilpas de øvrige felter

lat, long og post_url er på plads efter trin 2 og 3. Resten af blokken CONFIG øverst i scriptet:

  • additional_cost — ekstra omkostninger fra elselskabet, kr/kWh
  • vc_id — id’et på den virtuelle komponent. Sæt det til 0, hvis du ikke bruger nogen
  • control_enabledtrue, hvis en udgang skal styres efter prisen
  • switch_id — hvilken udgang der styres
  • price_limit — udgangen tændes under denne pris, kr/kWh
  • check_every_minutetrue henter prisen hvert minut. Brug det kun til test — det belaster den server, priserne hentes fra, og prisen ændrer sig alligevel kun én gang i timen
  • check_delay_seconds — hvor mange sekunder efter time- eller minutskiftet prisen hentes

Tryk Gem, når felterne er på plads.

5
Start

Start scriptet

Slå autostart til, og tryk på play. En blå ring om play-knappen betyder, at scriptet kører.

Script-listen med kontakten til autostart slået til og play-knappen markeret, som viser at scriptet kører
Tryk for at forstørre.
6
Kontrol

Tjek at det virker

Prisen står under EnergiElpris, så snart scriptet har hentet den første gang. Opdatér siden med F5, hvis den ikke er dukket op endnu.

Fanen Elpris under Energi med Live slået til og prisen vist som Live Elpris 2,31 DKK
Tryk for at forstørre.
Scriptets konsol med linjerne Netselskab cerius_c prisomraade DK2, Elpris nu 0.44 kr/kWh og Naeste prischeck om 60 sekunder
Åbner du scriptet fra enhedens eget webinterface, viser konsollen hvilket netselskab der blev fundet, og hvad prisen blev. Her er scriptet kørt i testtilstand med check_every_minute slået til — normalt står der en time til næste check.

Ekstra: vis prisen som en enhed i appen

Det kræver en Gen3-enhed eller nyere. Opret en virtuel komponent af typen nummer på enheden. Den første får altid id 200, den næste 201, den næste 202 og så videre. Skriv id’et i vc_id.

Dialogen Opret virtuel komponent med typen nummer valgt, og indstillingerne nedenunder hvor komponenten hedder number:200 og har Kr. som enhed
Tryk for at forstørre.

Opret derefter en gruppe, sæt hak i komponenten, og slå Udpak virtuel gruppe som enhed til.

Fanen Grupper med knappen Opret gruppe, og dialogen Opret virtuel gruppe hvor komponenten Elpris er sat hak i og Udpak virtuel gruppe som enhed er slået til
Tryk for at forstørre.

Prisen står nu som sin egen enhed ved siden af den enhed, den hører til.

Virker prisen ikke i appen eller på et Wall Display, så åbn scriptets konsol via enhedens IP-adresse, og se efter en fejl på Number.Set. Den hyppigste årsag er, at komponenten ikke findes, eller at id’et i vc_id ikke passer.

Appens rumoversigt med en Shelly Mini 1PM Gen3 ved siden af en virtuel enhed, der hedder Elpris og viser prisen i kroner
Tryk for at forstørre.

Ekstra: styr en udgang efter prisen

Sæt control_enabled til true, vælg udgangen med switch_id, og sæt grænsen med price_limit. Udgangen tændes, når prisen er under grænsen, og slukkes, når den er over.

Gulvvarme, varmt vand og elbilopladning er de rigtige belastninger: de er langsomme og tåler at vente. Lys og stikkontakter er ikke værd at styre på pris.

Kombinér prisstyringen med et skema i stedet for at lade den stå alene: skemaet bestemmer, hvornår styringen overhovedet må køre, og prisen bestemmer hvornår inden for det vindue. Og giv kunden mulighed for at overstyre manuelt.

Tips

  • Hent ikke prisen hvert minut. En gang i timen er nok, og hyppigere belaster en tjeneste, andre betaler for.
  • Sæt grænsen ud fra kundens faktiske regning, ikke ud fra spotprisen.
  • Hold øje med konsollen en dag, før du forlader installationen.