Dr. Smart Home cuts electricity bills with LUA
The New Beginning
As you might have read but probably didn't, I have been super satisfied with my LUA-made virtual weather station (you can catch up here part1, part2) and I have been itching about what to do next. After not a lot of consideration, I got an idea - what if I was able to bring current electricity price into 1Home and consequentially into my KNX & Matter based smart home. It could be super useful to have this information in the system - firstly for the satisfaction of having cracked this nut, secondly because it's cool to be able to see the current and past electricity prices but most importantly because it can help you make your home more energy efficient - or to be precise - more energy cost efficient.
How?
Well if you turn things on when electricity price is low and turn them off when the price is high - it makes sense that you will pay less then if you did it the other way around.
Finding API
Before we can actually start any money saving, we need to make everything work i.e. the current electricity price has to be available in the smart home system. The first step on the way to achieving this is to find if there is an online source (preferably free of charge) that can be scraped or a web API that returns the desired values. After a bit of research, I first found a public scrapable website - euenergy.live - with daily update energy prices for all EU member states. I wrote a short script that could fetch the page and parse the price from the text.
This was so easy I thought it's too good to be true. And of course, as it turns out in most such cases - it wasn't true. Or at least not in the way I wanted it to be. While I did get the energy price for my location into the system - this was only the average daily price of electricity, which is really not useful for any energy cost saving scenarios I had envisioned.
So I ditched euenrgy and after a bit more AI scanning I found another solution - a web api from Zyla API Hub. It looked very promising until the moment I found out I can do exactly nothing with it before I purchase the subscription. Not ideal for personal use and I was fairly sure that a free api has to exist so the hunt went on.
And sure enough, after some more digging I finally found something that looked like ENTSO-E, a transparency platform for electricity generation, transportation and consumption for the European market. It offered a free of charge web api, that returns the electricity price for a selected location.
AI can't help
After finding the API and getting the security token I got down to business with my verified method of LUA coding which is basically pasting the console output into a currently selected AI model until there are no errors. And while this recipe has always born results in the past, this time AI fell into a strange loop and couldn't get out of it, despite lot's of trying and switching between various LLM models. In the end I had to go about it the old school way. Open the API documentation, find the section with electricity pricing, copy the specified API call, paste it to my LUA, log the response, check the response - and then return to AI but with some basic understanding of how the call should be structured and what you actually want to get from the response. - now I didn't need the LLM to understand the ENTSO-E API but just to write all the lines of code based on my understanding of it. That worked and I have to say that going out of AI at first felt uncomfortable (because I had to turn on my brain) but after the job was done it brought additional satisfaction - compared to just leaning back and letting the computer do all the hard work.
After the basic script was working I just had to polish it a little bit, to account for the overflow at the end of the day (ENTSO-E returns values for one day from 00:01 to 24:00) and also for the daylight saving (and if anyone with any leverage on this is reading - please please, let's just cancel daylight saving.).
And voila, I had a script that gave me the current electricity price and the electricity price after next hour.
That's it! Is it?
And for a moment or two I thought that's it!! But suspiciously low prices made me think - is it really? And after checking with the computer i thought - of course it isn't! Turns out ENTSO-E returns the current electricity price - but it's not a household electricity price - it's a wholesale electricity price. This price can be vaguely related to the household equivalent and AI suggested I could just use that for shifting loads but I was not too happy about that. While I understood that when wholesale price is the lowest there is a strong chance that also the household kWh price is the lowest - I still felt like I failed in getting the actual (and at least approximately accurate) price to my smart home system.
After coming this far, I could not stop - but further research showed that getting the household electricity current price is MUCH more difficult and it heavily depends on where you live and who is your electricity supplier.
La cavo nostra
So let me break down what my research turned up for my location and my energy supplier. Firstly, I live in Ljubljana Slovenia and I pay my electricity in a single bill to a Slovenian electricity distribution company. This company takes a part of this (based on consumed energy) then pays a part of this to another company, who owns the infrastructure.
So the first part of the payment is based on the package I have - there is a "dynamic package" which apparently I am not using and there is a standard package that uses two tariffs - the high one and the low one.
The second part of the payment is called the "network fee" but it's not fully constant. To the best of my understanding this part of the payment is composed of two parts again - one constant based on the connected power & peaks in energy usage. Not 100 % how this is calcualted. The second part of this second part of the payment is again based on my usage but this time the actual price is defined by a 5 tariff system.
Complicated ha? I am PhD in electrical science and have been dealing with related areas all my professional life and I find it difficult to understand. Not turning this into a political topic or a conspiracy theory but let it be clear that in my very strong opinion this unnecessarily complicated system is purposely built so that a regular person does not fully understand it. La cavo nostra.
Sure enough there are no API'S to be found anywhere. Instead there is a PDF document on each of the company pages. By uploading this document and a photo of my electricity bill to AI - I was able to determine all tariffs for both companies and the periods of day they are active in. I also managed to find out this changes rarely (for example for one of the companies the current price list is valid from 1.1. 2024) and usually not by a lot. So I decided to "hard-code" the values and the time periods into my existing code and have it return the pricing for this and for next hour.
So the final solution is a bit hacked together, probably not 100 % accurate and it has to be updated manually probably at least once per year - but it does it job. I think the energy billing & distribution system in Slovenia is among the worst in Europe from the perspective of grid flexibility. So if you are living elsewhere you might not have to endure the same pains I did. In Latvia for example (correct me if I'm wrong Zandis Zvirbulis ), you have a 15 minute change in electricity price and you get that as one set of information - forecasted for 24 hours in advance.
Here is the entire code:
-- ===============================================================
-- Fetch ENTSO-E Day-Ahead Prices (A44)
-- ===============================================================
-- API Documentation: https://transparency.entsoe.eu/
-- Requires a valid API token (inserted below)
-- ===============================================================
-- Define ENTSO-E API endpoint and parameters
local now = time.now()
log.info(now)
-- ===============================================================
-- Generate yesterday/today ENTSO-E periodStart / periodEnd
-- using only supported time functions
-- ===============================================================
local now = time.now()
log.info(now)
-- current date parts
local y, m, d = now.Year, now.Month, now.Day
-- helper function: get previous/next day safely
local function shiftDay(y, m, d, diff)
local daysInMonth = {31,28,31,30,31,30,31,31,30,31,30,31}
local isLeap = (y % 400 == 0) or (y % 4 == 0 and y % 100 ~= 0)
if isLeap then daysInMonth[2] = 29 end
d = d + diff
if d < 1 then
m = m - 1
if m < 1 then m = 12; y = y - 1 end
d = daysInMonth[m]
elseif d > daysInMonth[m] then
d = 1
m = m + 1
if m > 12 then m = 1; y = y + 1 end
end
return y, m, d
end
-- yesterday and today dates
local y1, m1, d1 = shiftDay(y, m, d, -1)
local y2, m2, d2 = y, m, d
-- build ENTSO-E format (UTC 22:00)
local periodStart = string.format("%04d%02d%02d2200", y1, m1, d1)
local periodEnd = string.format("%04d%02d%02d2200", y2, m2, d2)
log.info("periodStart = %s", periodStart)
log.info("periodEnd = %s", periodEnd)
local baseUrl = "https://web-api.tp.entsoe.eu/api"
local parameters = {
documentType = "A44", -- Day-ahead prices
in_Domain = "10YSI-ELES-----O", -- Austria (APG)
out_Domain = "10YSI-ELES-----O",
periodStart = periodStart, -- Start UTC (YYYYMMDDHHMM)
periodEnd = periodEnd, -- End UTC (YYYYMMDDHHMM)
securityToken = "YOUR TOKEN HERE" -- Your ENTSO-E token
}
-- Build the query string
local query = ""
for key, value in pairs(parameters) do
if query == "" then
query = "?" .. key .. "=" .. value
else
query = query .. "&" .. key .. "=" .. value
end
end
local url = baseUrl .. query
log.info("Requesting ENTSO-E data from: %v", url)
-- Send the HTTP request
local response, statusCode, error = http.get(url, {
responseBodyType = http.bodyType.STRING, -- Keep XML as plain text
})
-- Handle errors and responses
if error then
log.error("HTTP error: %v", error)
return
end
log.info("HTTP status: %v", statusCode)
log.info("Response body: %v", response)
-- ===============================================================
-- Extract current and next hour electricity price (€/kWh, UTC-corrected)
-- and set outputs
-- ===============================================================
-- Parse all <price.amount> values manually using string.find
local prices = {}
local startPos = 1
while true do
local startTag, endTag = string.find(response, "<price.amount>", startPos)
if not startTag then break end
local closeTag = string.find(response, "</price.amount>", endTag + 1)
if not closeTag then break end
local value = string.sub(response, endTag + 1, closeTag - 1)
table.insert(prices, tonumber(value))
startPos = closeTag + 1
end
log.info("Parsed %v price values", #prices)
-- -------------------------------------------
-- Determine UTC offset (DST correction)
-- -------------------------------------------
local month = now.Month
local tz_offset = 1 -- default wintertime (UTC+1)
if month >= 4 and month <= 10 then
tz_offset = 2 -- summertime (UTC+2)
end
-- Convert local hour to UTC
local utc_hour = now.Hour - tz_offset
if utc_hour < 0 then utc_hour = utc_hour + 24 end
if utc_hour >= 24 then utc_hour = utc_hour - 24 end
local utc_next = utc_hour + 1
if utc_next >= 24 then utc_next = 0 end
-- Position numbers (ENTSO-E uses 1-based, 1 = 00:00-01:00)
local position_current = utc_hour + 1
local position_next = utc_next + 1
-- Ensure within bounds
if position_next > #prices then position_next = #prices end
-- Get prices in €/MWh
local Ecurrent = prices[position_current]
local Enext = prices[position_next]
if Ecurrent == nil or Enext == nil then
log.error("Missing price data for positions %v / %v", position_current, position_next)
return
end
-- Convert to €/kWh
local Ecurrent_kWh = Ecurrent / 1000
local Enext_kWh = Enext / 1000
log.info("Local hour %v | UTC hour %v", now.Hour, utc_hour)
log.info("Current (local %v:00, UTC %v:00): %.2f €/MWh | %.5f €/kWh",
now.Hour, utc_hour, Ecurrent, Ecurrent_kWh)
log.info("Next (local %v:00, UTC %v:00): %.2f €/MWh | %.5f €/kWh",
now.Hour + 1, utc_next, Enext, Enext_kWh)
-- Set outputs (€/kWh)
local err = OUTPUTS.Ecurrent.set(Ecurrent_kWh)
if err then log.error(err) end
err = OUTPUTS.Enext.set(Enext_kWh)
if err then log.error(err) end
-- ===============================================================
-- Compute current household electricity price (GEN-I + network)
-- incl. 22% VAT, based on local time
-- ===============================================================
local now = time.now()
log.info("Time for price calculation: %v", now)
-- Define combined prices with VAT (€/kWh)
local priceTable = {
{startH=0, endH=6, price=0.18942}, -- NT + OB1
{startH=6, endH=8, price=0.21532}, -- VT + OB2
{startH=8, endH=14, price=0.22081}, -- VT + OB3
{startH=14, endH=18, price=0.22580}, -- VT + OB4
{startH=18, endH=22, price=0.23280}, -- VT + OB5
{startH=22, endH=24, price=0.19336} -- NT + OB2
}
-- Helper: find current price slot
local function findPrice(hour)
for i, p in ipairs(priceTable) do
if hour >= p.startH and hour < p.endH then
return p.price, i
end
end
-- default safeguard
return priceTable[#priceTable].price, #priceTable
end
-- Current and next interval
local currentPrice, idx = findPrice(now.Hour)
local nextIdx = idx + 1
if nextIdx > #priceTable then nextIdx = 1 end
local nextPrice = priceTable[nextIdx].price
log.info("Current hour block: %02d.00–%02d.00, Price: %.5f €/kWh",
priceTable[idx].startH, priceTable[idx].endH, currentPrice)
log.info("Next block: %02d.00–%02d.00, Price: %.5f €/kWh",
priceTable[nextIdx].startH, priceTable[nextIdx].endH, nextPrice)
-- Write outputs
local err = OUTPUTS.EHcurrent.set(currentPrice)
if err then log.error(err) end
err = OUTPUTS.EHnext.set(nextPrice)
if err then log.error(err) end
log.info("EHcurrent=%.5f €/kWh ; EHnext=%.5f €/kWh", currentPrice, nextPrice) Wrapping up
What was left was the classical procedure of writing the newly acquired values to KNX group addresses then creating virtual Generic devices for each of these values (current_price_wholesale, next_price_wholesale, current_price_household, next_price_household), which I did with a an action block in the 1Home automations.

In the "Graphs" section I have created a chart displaying the current wholesale and the current household electricity price:

And last but not least - I put those 4 devices in "Utility" and now I can always check the current and next electricity prices in my 1Home App.

And going forward having electricity prices in your smart home is a nice thing but to actually cut the electricity we now have to act on it. Schedule big loads into lower price periods and voila. Not a part of this article though...
Dr. SmartHome signing out! May your circuits be smart and your energy bills be ever so light.