We have rewritten script (old script will not work properly), because of a change
in time intervals (previously electricity price was per hour, now per 15 minutes).
Here is a new script:
Code:
https = require('ssl.https')
json = require('json')
ltn12 = require('ltn12')
country = 'lv' -- Latvia
--country = 'fi' -- Finland
--country = 'lt' -- Lithuania
--country = 'ee' -- Estonia
current_price_addr = '40/1/18'
cheapest_hour_addr = '40/1/16'
cheapest_hour_price_addr = '40/1/17'
costliest_hour_addr = '40/1/19'
cosltiest_hour_price_addr = '40/1/20'
cheapest_time_addr = '40/1/14'
cheapest_price_addr = '40/1/15'
costliest_time_addr = '40/1/21'
costliest_price_addr = '40/1/22'
current_date = os.date('*t')
local day_start_ts = os.time({year = current_date.year, month = current_date.month, day = current_date.day, hour = 0, min = 0, sec = 0})
local day_end_ts = os.time({year = current_date.year, month = current_date.month, day = current_date.day, hour = 23, min = 59, sec = 59})
local current_ts = os.time()
previous_date = os.date('%F', os.time() - 24*60*60)
next_date = os.date('%F', os.time() + 24*60*60)
url = 'https://dashboard.elering.ee/api/nps/price?start='.. previous_date ..'T00%3A00%3A00.999Z&end=' .. next_date ..'T00%3A00%3A00.999Z'
mac = 0
io.readfile('/sys/class/net/eth0/address'):gsub('%x%x', function(v)
mac = mac * 256 + tonumber(v, 16)
end)
response = {}
res, code = https.request({
url = url,
protocol = 'tlsv12',
headers = {
['user-agent'] = 'LM ' .. mac
},
sink = ltn12.sink.table(response)
})
if res and code == 200 then
data = json.pdecode(table.concat(response))
else
log('request error', res, code)
return
end
min_price = 99999
max_price = -1
min_price_timestamp = 0
max_price_timestamp = 0
current_price = nil
hourly_prices = {}
for h = 0, 23 do
hourly_prices[h] = { sum = 0, count = 0 }
end
if not data or not data.data or not data.data[country] then
log('No data for country: ' .. country)
return
end
for _, record in ipairs(data.data[country]) do
local timestamp = record.timestamp
local price = record.price
if timestamp >= day_start_ts and timestamp <= day_end_ts then
if price < min_price then
min_price = price
min_price_timestamp = timestamp
end
if price > max_price then
max_price = price
max_price_timestamp = timestamp
end
local record_time = os.date('*t', timestamp)
local hour = record_time.hour
if hourly_prices[hour] then
hourly_prices[hour].sum = hourly_prices[hour].sum + price
hourly_prices[hour].count = hourly_prices[hour].count + 1
end
end
if current_ts >= timestamp and current_ts < (timestamp + 15 * 60) then
current_price = price
end
end
--log('--- Current Status ---')
if current_price then
--log('Current 15-min price: ' .. current_price .. ' EUR/MWh')
grp.write(current_price_addr, current_price) -- float
else
log('Could not determine current price')
end
min_avg_price = 99999
max_avg_price = -1
cheapest_hour = -1
costliest_hour = -1
for hour, data in pairs(hourly_prices) do
if data.count > 0 then
local avg_price = data.sum / data.count
--log(string.format("Hour %02d:00 - Avg Price: %.2f", hour, avg_price))
if avg_price < min_avg_price then
min_avg_price = avg_price
cheapest_hour = hour
end
if avg_price > max_avg_price then
max_avg_price = avg_price
costliest_hour = hour
end
end
end
cheapest_hour_time = {}
cheapest_hour_time['wday'] = current_date['wday']
--log('--- Cheapest Hour Today ---')
if cheapest_hour ~= -1 then
--log(string.format('Hour: %02d:00 - %02d:59', cheapest_hour, cheapest_hour))
--log(string.format('Average Price: %.2f EUR/MWh', min_avg_price))
cheapest_hour_time['hour'] = cheapest_hour
grp.write(cheapest_hour_addr, cheapest_hour_time)
grp.write(cheapest_hour_price_addr, min_avg_price)
else
log('Could not calculate the cheapest hour.')
end
costliest_hour_time = {}
costliest_hour_time['wday'] = current_date['wday']
--log('--- Costliest Hour Today ---')
if costliest_hour ~= -1 then
--log(string.format('Hour: %02d:00 - %02d:59', costliest_hour, costliest_hour))
--log(string.format('Average Price: %.2f EUR/MWh', max_avg_price))
costliest_hour_time['hour'] = costliest_hour
grp.write(costliest_hour_addr, costliest_hour_time)
grp.write(cosltiest_hour_price_addr, max_avg_price)
else
log('Could not calculate the costliest hour.')
end
cheapest_time = {}
cheapest_time['wday'] = current_date['wday']
--log('--- Cheapest 15-min Interval Today ---')
if min_price_timestamp > 0 then
local min_price_time = os.date('*t', min_price_timestamp)
--log('Price: ' .. min_price .. ' EUR/MWh')
--log('Time: ' .. string.format('%02d:%02d', min_price_time.hour, min_price_time.min))
cheapest_time['hour'] = min_price_time.hour
cheapest_time['minute'] = min_price_time.min
grp.write(cheapest_time_addr, cheapest_time)
grp.write(cheapest_price_addr, min_price)
else
log('Could not find minimum price for today')
end
costliest_time = {}
costliest_time['wday'] = current_date['wday']
--log('--- Costliest 15-min Interval Today ---')
if max_price_timestamp > 0 then
local max_price_time = os.date('*t', max_price_timestamp)
--log('Price: ' .. max_price .. ' EUR/MWh')
--log('Time: ' .. string.format('%02d:%02d', max_price_time.hour, max_price_time.min))
costliest_time['hour'] = max_price_time.hour
costliest_time['minute'] = max_price_time.min
grp.write(costliest_time_addr, costliest_time)
grp.write(costliest_price_addr, max_price)
else
log('Could not find maximum price for today')
end
We have rewritten script (old script will not work properly), because of a change
in time intervals (previously electricity price was per hour, now per 15 minutes).
Here is a new script:
Code:
https = require('ssl.https')
json = require('json')
ltn12 = require('ltn12')
country = 'lv' -- Latvia
--country = 'fi' -- Finland
--country = 'lt' -- Lithuania
--country = 'ee' -- Estonia
current_price_addr = '40/1/18'
cheapest_hour_addr = '40/1/16'
cheapest_hour_price_addr = '40/1/17'
costliest_hour_addr = '40/1/19'
cosltiest_hour_price_addr = '40/1/20'
cheapest_time_addr = '40/1/14'
cheapest_price_addr = '40/1/15'
costliest_time_addr = '40/1/21'
costliest_price_addr = '40/1/22'
current_date = os.date('*t')
local day_start_ts = os.time({year = current_date.year, month = current_date.month, day = current_date.day, hour = 0, min = 0, sec = 0})
local day_end_ts = os.time({year = current_date.year, month = current_date.month, day = current_date.day, hour = 23, min = 59, sec = 59})
local current_ts = os.time()
previous_date = os.date('%F', os.time() - 24*60*60)
next_date = os.date('%F', os.time() + 24*60*60)
url = 'https://dashboard.elering.ee/api/nps/price?start='.. previous_date ..'T00%3A00%3A00.999Z&end=' .. next_date ..'T00%3A00%3A00.999Z'
mac = 0
io.readfile('/sys/class/net/eth0/address'):gsub('%x%x', function(v)
mac = mac * 256 + tonumber(v, 16)
end)
response = {}
res, code = https.request({
url = url,
protocol = 'tlsv12',
headers = {
['user-agent'] = 'LM ' .. mac
},
sink = ltn12.sink.table(response)
})
if res and code == 200 then
data = json.pdecode(table.concat(response))
else
log('request error', res, code)
return
end
min_price = 99999
max_price = -1
min_price_timestamp = 0
max_price_timestamp = 0
current_price = nil
hourly_prices = {}
for h = 0, 23 do
hourly_prices[h] = { sum = 0, count = 0 }
end
if not data or not data.data or not data.data[country] then
log('No data for country: ' .. country)
return
end
for _, record in ipairs(data.data[country]) do
local timestamp = record.timestamp
local price = record.price
if timestamp >= day_start_ts and timestamp <= day_end_ts then
if price < min_price then
min_price = price
min_price_timestamp = timestamp
end
if price > max_price then
max_price = price
max_price_timestamp = timestamp
end
local record_time = os.date('*t', timestamp)
local hour = record_time.hour
if hourly_prices[hour] then
hourly_prices[hour].sum = hourly_prices[hour].sum + price
hourly_prices[hour].count = hourly_prices[hour].count + 1
end
end
if current_ts >= timestamp and current_ts < (timestamp + 15 * 60) then
current_price = price
end
end
--log('--- Current Status ---')
if current_price then
--log('Current 15-min price: ' .. current_price .. ' EUR/MWh')
grp.write(current_price_addr, current_price) -- float
else
log('Could not determine current price')
end
min_avg_price = 99999
max_avg_price = -1
cheapest_hour = -1
costliest_hour = -1
for hour, data in pairs(hourly_prices) do
if data.count > 0 then
local avg_price = data.sum / data.count
--log(string.format("Hour %02d:00 - Avg Price: %.2f", hour, avg_price))
if avg_price < min_avg_price then
min_avg_price = avg_price
cheapest_hour = hour
end
if avg_price > max_avg_price then
max_avg_price = avg_price
costliest_hour = hour
end
end
end
cheapest_hour_time = {}
cheapest_hour_time['wday'] = current_date['wday']
--log('--- Cheapest Hour Today ---')
if cheapest_hour ~= -1 then
--log(string.format('Hour: %02d:00 - %02d:59', cheapest_hour, cheapest_hour))
--log(string.format('Average Price: %.2f EUR/MWh', min_avg_price))
cheapest_hour_time['hour'] = cheapest_hour
grp.write(cheapest_hour_addr, cheapest_hour_time)
grp.write(cheapest_hour_price_addr, min_avg_price)
else
log('Could not calculate the cheapest hour.')
end
costliest_hour_time = {}
costliest_hour_time['wday'] = current_date['wday']
--log('--- Costliest Hour Today ---')
if costliest_hour ~= -1 then
--log(string.format('Hour: %02d:00 - %02d:59', costliest_hour, costliest_hour))
--log(string.format('Average Price: %.2f EUR/MWh', max_avg_price))
costliest_hour_time['hour'] = costliest_hour
grp.write(costliest_hour_addr, costliest_hour_time)
grp.write(cosltiest_hour_price_addr, max_avg_price)
else
log('Could not calculate the costliest hour.')
end
cheapest_time = {}
cheapest_time['wday'] = current_date['wday']
--log('--- Cheapest 15-min Interval Today ---')
if min_price_timestamp > 0 then
local min_price_time = os.date('*t', min_price_timestamp)
--log('Price: ' .. min_price .. ' EUR/MWh')
--log('Time: ' .. string.format('%02d:%02d', min_price_time.hour, min_price_time.min))
cheapest_time['hour'] = min_price_time.hour
cheapest_time['minute'] = min_price_time.min
grp.write(cheapest_time_addr, cheapest_time)
grp.write(cheapest_price_addr, min_price)
else
log('Could not find minimum price for today')
end
costliest_time = {}
costliest_time['wday'] = current_date['wday']
--log('--- Costliest 15-min Interval Today ---')
if max_price_timestamp > 0 then
local max_price_time = os.date('*t', max_price_timestamp)
--log('Price: ' .. max_price .. ' EUR/MWh')
--log('Time: ' .. string.format('%02d:%02d', max_price_time.hour, max_price_time.min))
costliest_time['hour'] = max_price_time.hour
costliest_time['minute'] = max_price_time.min
grp.write(costliest_time_addr, costliest_time)
grp.write(costliest_price_addr, max_price)
else
log('Could not find maximum price for today')
end
Anyone had any luck or experience? Can you please share scripts for getting NordPool prices into logic machine?
Hi!
I do two scripts
Both "Scheduled" Script 1 -
Collecting electric price from API. running 4 times/day hour 17,18,21,23 amd minute 5.
Code:
CODE
-- API-skript (spara per datum)
local json = require("json")
local ltn12 = require("ltn12")
local http = require("socket.http")
local DECIMALS = 5
local PRISKLASS = "SE2"
local function fetch_prices(date_timestamp)
local year = os.date("%Y", date_timestamp)
local month_day = os.date("%m-%d", date_timestamp)
local url = string.format("https://www.elprisetjustnu.se/api/v1/prices/%s/%s_%s.json", year, month_day, PRISKLASS)
local resp = {}
local res, code = http.request({url = url, method = "GET", sink = ltn12.sink.table(resp)})
if code == 200 then
local decoded = json.pdecode(table.concat(resp))
if decoded then
local prices = {}
for i,v in ipairs(decoded) do
local time_start = v.time_start -- "2025-09-10T21:15:00+02:00"
local hh = string.sub(time_start, 12, 13)
local mm = string.sub(time_start, 15, 16)
local val = tonumber(string.format("%." .. DECIMALS .. "f", v.SEK_per_kWh))
if mm == "00" then
-- timpris: duplicera till kvart
for _, q in ipairs({"00","15","30","45"}) do
prices[hh .. ":" .. q] = val
end
else
prices[hh .. ":" .. mm] = val
end
end
return prices
end
end
return nil
end
local function save_prices()
local now = os.time()
-- idag och imorgon som timestamps (midnatt)
local t = os.date("*t", now)
t.hour, t.min, t.sec = 0,0,0
local idag_ts = os.time(t)
local imorgon_ts = idag_ts + 86400
-- Spara dagens priser i storage med datumnyckel
local ds_idag = os.date("%Y-%m-%d", idag_ts)
local p_idag = fetch_prices(idag_ts)
if p_idag then
storage.set("elpris_" .. ds_idag, json.encode(p_idag))
log("Sparade elpriser för " .. ds_idag)
else
log("️ Kunde inte hämta priser för " .. ds_idag)
end
-- Spara morgondagens priser (förutsatt kl 18+ eller när du vill hämta)
if tonumber(os.date("%H", now)) >= 18 then
local ds_im = os.date("%Y-%m-%d", imorgon_ts)
local p_im = fetch_prices(imorgon_ts)
if p_im then
storage.set("elpris_" .. ds_im, json.encode(p_im))
log("Sparade elpriser för " .. ds_im)
else
log("️ Kunde inte hämta priser för " .. ds_im)
end
end
-- Rensa gamla nycklar (behåll t.ex. -3..+2 dagar)
for i = 4, 30 do
local old = os.date("%Y-%m-%d", idag_ts - i*86400)
storage.set("elpris_" .. old, nil)
end
end
save_prices()
Script 2
Writing current electrical price to KNX address
Running as scheduled minute 0,15,30,45
Code:
local json = require("json")
local GA_ELPRIS = "34/4/99"
local function round_to_quarter()
local now = os.time()
local hh = tonumber(os.date("%H", now))
local min = tonumber(os.date("%M", now))
min = math.floor(min / 15) * 15
return string.format("%02d:%02d", hh, min)
end
-- försök hitta pris i dataset för en specifik datumnyckel
local function find_in_date(date_str, hhmm)
local key = "elpris_" .. date_str
local j = storage.get(key)
if not j then return nil end
local prices = json.decode(j)
if not prices then return nil end
local p = prices[hhmm]
if p then return p, key end
-- fallback: sök närmaste tidigare kvart inom detta dataset
local hh = tonumber(string.sub(hhmm,1,2))
local mm = tonumber(string.sub(hhmm,4,5))
local kvart_list = {0,15,30,45}
for h = hh, 0, -1 do
for i = #kvart_list, 1, -1 do
local q = kvart_list[i]
if (h < hh) or (q <= mm) then
local try = string.format("%02d:%02d", h, q)
if prices[try] then
return prices[try], key, try
end
end
end
end
return nil
end
-- Huvudfunktion som provar datum i prioriterad ordning
local function get_price_for_quarter()
local now = os.time()
local hhmm = round_to_quarter()
local date_today = os.date("%Y-%m-%d", now)
local date_tomorrow = os.date("%Y-%m-%d", now + 86400)
local date_yesterday = os.date("%Y-%m-%d", now - 86400)
local candidates = {date_today, date_tomorrow, date_yesterday}
for _, d in ipairs(candidates) do
local p, used_key, used_q = find_in_date(d, hhmm)
if p then
-- used_q kan vara nil om exakt hhmm träffade; annars är used_q den kvarten vi använde
local used = used_q or hhmm
return p, used, used_key
end
end
return nil, hhmm, nil
end
-- Anropa och skriv till KNX
local pris, used_hhmm, used_key = get_price_for_quarter()
if pris then
local pris_5dec = string.format("%.5f", pris)
log(string.format("Skriver elpris kl %s (från %s): %s kr/kWh", used_hhmm, used_key or "ingen", pris_5dec))
grp.write(GA_ELPRIS, tonumber(pris_5dec))
else
log("️ Ingen elprisinformation hittades för " .. used_hhmm)
end
Here is a new script for a hourly pricing for Latvia, Finland, Lithuania, Estonia
Code:
country = 'lv' -- Latvia
--country = 'fi' -- Finland
--country = 'lt' -- Lithuania
--country = 'ee' -- Estonia
current_hour_price_addr = '43/1/2'
cheapest_hour_addr = '43/1/3'
cheapest_hour_price_addr = '43/1/4'
costliest_hour_addr = '43/1/5'
costliest_hour_price_addr = '43/1/6'
local current_date = os.date('*t')
local day_start_ts = os.time({year = current_date.year, month = current_date.month, day = current_date.day, hour = 0, min = 0, sec = 0})
local day_end_ts = os.time({year = current_date.year, month = current_date.month, day = current_date.day, hour = 23, min = 59, sec = 59})
local previous_date = os.date('%F', os.time() - 24 * 60 * 60)
local next_date = os.date('%F', os.time() + 24 * 60 * 60)
local url = 'https://dashboard.elering.ee/api/nps/price?start=' .. previous_date .. 'T00%3A00%3A00.999Z&end=' .. next_date .. 'T00%3A00%3A00.999Z'
--log(url)
local res, code = require('socket.http').request(url)
if not (res and code == 200) then
log('request error: ', res, code)
return
end
local data = require('json').pdecode(res)
if not data or not data.data or not data.data[country] then
log('No data for country: ' .. country)
return
end
local hourly_prices = {}
for h = 0, 23 do
hourly_prices[h] = { sum = 0, count = 0 }
end
for _, record in ipairs(data.data[country]) do
local timestamp = record.timestamp
local price = record.price
if timestamp >= day_start_ts and timestamp <= day_end_ts then
local record_time = os.date('*t', timestamp)
local hour = record_time.hour
if hourly_prices[hour] then
hourly_prices[hour].sum = hourly_prices[hour].sum + price
hourly_prices[hour].count = hourly_prices[hour].count + 1
end
end
end
local min_avg_price = 99999
local max_avg_price = -1
local cheapest_hour = -1
local costliest_hour = -1
local current_hour_avg_price = nil
for hour, price_data in pairs(hourly_prices) do
if price_data.count > 0 then
local avg_price = price_data.sum / price_data.count
log(string.format("Average price per hour %02d:00 - %.2f EUR/MWh", hour, avg_price))
if avg_price < min_avg_price then
min_avg_price = avg_price
cheapest_hour = hour
end
if avg_price > max_avg_price then
max_avg_price = avg_price
costliest_hour = hour
end
if hour == current_date.hour then
current_hour_avg_price = avg_price
end
end
end
if current_hour_avg_price then
log(string.format('Price for current hour (%02d:00): %.2f EUR/MWh', current_date.hour, current_hour_avg_price))
grp.write(current_hour_price_addr, current_hour_avg_price)
end
if cheapest_hour ~= -1 then
log(string.format('Cheapest hour: %02d:00, Price: %.2f EUR/MWh', cheapest_hour, min_avg_price))
local cheapest_hour_time = { wday = current_date.wday, hour = cheapest_hour }
grp.write(cheapest_hour_addr, cheapest_hour_time)
grp.write(cheapest_hour_price_addr, min_avg_price)
else
log('Could not calculate the cheapest hour.')
end
if costliest_hour ~= -1 then
log(string.format('Costliest hour: %02d:00, Price: %.2f EUR/MWh', costliest_hour, max_avg_price))
local costliest_hour_time = { wday = current_date.wday, hour = costliest_hour }
grp.write(costliest_hour_addr, costliest_hour_time)
grp.write(costliest_hour_price_addr, max_avg_price)
else
log('Could not calculate the costliest hour.')
end