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.
// 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();






