script to load ENTSOE day ahead electricity prices

Easy to use, 100% Lua-based event scripting framework.

Moderator: leecollings

willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

script to load ENTSOE day ahead electricity prices

Post by willemd »

Below is a script (for re-use) to load day-ahead electricity prices into domoticz historic variables.

Data is published by the EU transparency website. These are the base prices for dynamic electricity pricing contracts. Your provider will add surcharges and taxes to it. Please adapt it to your needs (for example country), as per documentation.

note: an example to get the dynamic prices from a electricty provider in JSON format is available here at this post:
viewtopic.php?p=294965#p294965

Code: Select all

-- Script to load day-ahead electricity prices into Domoticz historic variables, as a base for further processing.
-- New prices are available every day at 15:00 for the coming 24 hrs.
-- These are base prices. Providers of dynamic electricity contracts will add their surcharges and government taxes to it.
-- API documentation at: https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html
-- To get this data yourself (it is free):
--    1) Register for an account at https://transparency.entsoe.eu/dashboard/show
--    2) Send an email with subject "Restful API access" and your account email address in the body to [email protected]
--    3) After receipt of their confirmation, go into your account and generate your token.
--    4) Store the token in a domoticz user variable and note down the idx.
--    5) Adapt the idx on the line below.

local idxEUtoken=12  -- idx of the user variable with the API token

return {
	on = {
		timer = {
		    'at 15:05'  -- Adapt timer to your needs. Normally new prices are available after 15:00. 
		},
		httpResponses = {
			'EUprices' -- must match with the callback passed to the openURL command in the code below
		}
	},
	data =  {
            ElDayPrices               = { history = true, maxItems = 24 }
    },
	logging = {
		level = domoticz.LOG_INFO,
		marker = 'EU day ahead electricity prices',
	},
	execute = function(domoticz, item)
	    
	    local ElDayPrices = domoticz.data.ElDayPrices
	    
	    if (item.isTimer) then
	        -- section to launch the API get request 
	        local UrlStart='https://web-api.tp.entsoe.eu/api?'  -- the API website
	        local EUtoken=domoticz.variables(idxEUtoken)        -- user variable with API token
	        local DocType='A44'                                 -- day ahead prices document type 
	        local PriceRegion='10YNL----------L'                -- region is set to The Netherlands (adapt to your need as per API documentation)
	        local dayahead=0
    	        if tonumber(os.date("%H"))>=15 then                 -- day ahead available after 15:00, otherwise retrieve current day, depending on the time this script is launched
    	            dayahead=24*60*60  -- seconds for full day ahead
    	        end     
    	        local PricePeriodStart=os.date("%Y%m%d",os.time()+ dayahead) .. "0000"  -- range 00:00 to 23:00, this will return full day anyway
    	        local PricePeriodEnd=os.date("%Y%m%d", os.time()+ dayahead) .. "2300"    -- depending on time the script is launched, get current day or tomorrow's data
	        
	        -- compose the full URL
	        local EUurl=UrlStart .. 'securityToken=' .. EUtoken.value .. '&documentType=' .. DocType .. '&in_Domain=' .. PriceRegion .. '&out_Domain=' .. PriceRegion .. '&periodStart=' .. PricePeriodStart .. '&periodEnd=' .. PricePeriodEnd
	        
	        -- launch the URL
			domoticz.openURL({
				url = EUurl,
				method = 'GET',
				callback = 'EUprices', -- must match httpResponses above
			})
		else 
		    if (item.isHTTPResponse) then
		        -- response to openURL (HTTP GET) request was received
			    if (item.ok) then
			        if (item.isXML) then   -- should be XML
			            -- domoticz.utils.dumpTable(item.xml)  -- dumpTable can be used for debugging
			            
			            ElDayPrices.reset()  -- remove historic prices from previous run
			            
			            for id = 1, 24 do
                            domoticz.log('adding price : ' .. item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'],domoticz.LOG_INFO)
                            ElDayPrices.add(item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'])
                        end
			            
			            -- here you can add any further processing of these variables 
			            
			        else
				        domoticz.log('No XML received', domoticz.LOG_INFO)
				    end
			    else
				    domoticz.log('There was a problem handling the request. Item not ok', domoticz.LOG_INFO)
				    domoticz.log(item, domoticz.LOG_INFO)
			    end
		    end
	    end
	end
}

Last edited by willemd on Wednesday 21 December 2022 14:11, edited 1 time in total.
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

Here is the script updated to load both dynamic electricity prices (change once per hour, published after 15:00 for full next day) and gas prices (change once per day, published after 00:00 for current day and few hours next day) and calculate the costs.

Please adapt for your own country.

Code: Select all

-- Script to load day-ahead electricity prices into Domoticz historic variables, as a base for further processing.
-- New prices are available every day at 15:00 for the coming 24 hrs.
-- These are base prices. Providers of dynamic electricity contracts will add their surcharges and government taxes to it.
-- API documentation at: https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html
-- To get this data yourself (it is free):
--    1) Register for an account at https://transparency.entsoe.eu/dashboard/show
--    2) Send an email with subject "Restful API access" and your account email address in the body to [email protected]
--    3) After receipt of their confirmation, go into your account and generate your token.
--    4) Store the token in a domoticz user variable and note down the idx.
--    5) Adapt the idx on the line below.

-- devices
local idxElectricityP1=1
local idxGasP1=2

local idxCurrentDynamicElPrice=102
local idxDynamicElCosts=103
local idxDynamicElCostsCntr=104  -- counters are just added for testing, work fine as long as costs are positive

local idxCurrentDynamicGasPrice=107
local idxDynamicGasCosts=108
local idxDynamicGasCostsCntr=109 -- counters are just added for testing, work fine as long as costs are positive

-- user variables
local idxEUtoken=12  -- idx of the user variable with the API token
local idxBTWpercentage=13 -- user variable with BTW/VAT percentage
local idxDayElTransport=3 -- user variable for fixed daily transport costs
local idxMonthElFixed=8   -- user variable for fixed monthly costs
local idxDayGasTransport=6 -- user variable for fixed daily transport costs
local idxMonthGasFixed=8   -- user variable for fixed monthly costs

return {
	on = {
		timer = {
		    'at *:45',  -- Timer to get new electricity prices. Adapt timer to your needs. Normally new electricty prices are available after 15:00. 
		                -- Timer to get new gas prices. Prices received are in UTC so 1 hour shift needed for me.    
		                -- Prices are now re-loaded every hour, to avoid using old prices when there is one error loading once per day.
		    'at *:59',   -- trigger to calculate the costs of the past hour (missing 1 minute, added to next hour, except end of day)
		    'at *:01'    -- trigger to copy price from list (24 historic variables) to device with current price
		},
		httpResponses = {
			'EUprices', -- must match with the callback passed to the openURL command in the code below
			'EZGASprices',
		}
	},
	data =  {
            ElDayPrices               = { history = true, maxItems = 24 },
            GasDayPrices              = { history = true, maxItems = 24 },
            PreviousHrCumElCosts      = { initial = 0},
            PreviousHrCumElNett       = { initial = 0},
            PreviousHrCumGasCosts      = { initial = 0},
            PreviousHrCumGasNett       = { initial = 0}
    },
	logging = {
		level = domoticz.LOG_INFO,
		marker = 'EU and EZ day ahead prices',
	},
	execute = function(domoticz, item)
	    
	    local ElDayPrices = domoticz.data.ElDayPrices
	    local GasDayPrices = domoticz.data.GasDayPrices
	    local todayEl = domoticz.devices(idxElectricityP1).counterToday
	    local todayElreturn = domoticz.devices(idxElectricityP1).counterDeliveredToday
	    local todayGas = domoticz.devices(idxGasP1).counterToday
	    
	    if (item.isTimer) then
	        if (item.trigger == 'at *:01') then
	            -- copy dynamic price to device holding the current price
	            local VarIndex=24-tonumber(os.date("%H")) -- last price of day is index 1
	            local CurrentElPrice=tonumber(ElDayPrices.get(VarIndex).data)/1000 -- from EURO/MWh to EURO/kWh
	            domoticz.log('Current Dynamic El price EURO/kWh :' .. CurrentElPrice,domoticz.LOG_INFO)
	            domoticz.devices(idxCurrentDynamicElPrice).updateCustomSensor(CurrentElPrice)
	            
	            -- also copy gas prices to current device
	            if tonumber(os.date("%H"))==0 then
	                VarIndex=1 
	            else
	                VarIndex=24-tonumber(os.date("%H"))+1
	            end     
	            local CurrentGasPrice=tonumber(GasDayPrices.get(VarIndex).data) -- Euro per m3, last price is for 00:00 to 01:00 next day.
	            domoticz.log('Current Dynamic Gas price :' .. CurrentGasPrice,domoticz.LOG_INFO)
	            domoticz.devices(idxCurrentDynamicGasPrice).updateCustomSensor(CurrentGasPrice)
	            
	            if (tonumber(os.date("%H")) == 0) then
	                domoticz.devices(idxDynamicElCosts).updateCustomSensor(domoticz.data.PreviousHrCumElCosts)
	                domoticz.devices(idxDynamicGasCosts).updateCustomSensor(domoticz.data.PreviousHrCumGasCosts)
	            end 
	            
	        else
	            if (item.trigger == 'at *:59') then
    	            -- first calculate cumulative daily total and last hour electricity usage
    	            local todayElNett=domoticz.devices(idxElectricityP1).counterToday-domoticz.devices(idxElectricityP1).counterDeliveredToday
    	            domoticz.log('counterToday :' .. domoticz.devices(idxElectricityP1).counterToday,domoticz.LOG_INFO)
    	            domoticz.log('counterDeliveredToday :' .. domoticz.devices(idxElectricityP1).counterDeliveredToday,domoticz.LOG_INFO)
    	            domoticz.log('todayElNett :' .. todayElNett,domoticz.LOG_INFO)
    	            local lastHrElNett = todayElNett - domoticz.data.PreviousHrCumElNett
    	            domoticz.log('lastHrElNett :' .. lastHrElNett,domoticz.LOG_INFO)
    	            -- then calculate the costs and add to device
    	            local lastHrElCosts = lastHrElNett * domoticz.devices(idxCurrentDynamicElPrice).sensorValue * (100 + domoticz.variables(idxBTWpercentage).value)/100
    	            domoticz.log('lastHrElCosts :' .. lastHrElCosts,domoticz.LOG_INFO)
    	            local CumElCosts = domoticz.utils.round(domoticz.data.PreviousHrCumElCosts + lastHrElCosts,2)
    	            domoticz.log('CumElCosts :' .. CumElCosts,domoticz.LOG_INFO)
    	            domoticz.devices(idxDynamicElCosts).updateCustomSensor(CumElCosts)
    	            
    	            local previousCounter=0 
		            local divider=100
		            previousCounter=domoticz.devices(idxDynamicElCostsCntr).counter
		            --domoticz.devices(idxDynamicElCostsCntr).updateCounter(lastHrElCosts*divider + previousCounter*divider)
		            domoticz.devices(idxDynamicElCostsCntr).updateCounter(CumElCosts*divider)
		            domoticz.log('previousCounter :' .. previousCounter,domoticz.LOG_INFO)
		            domoticz.log('newcounter :' .. lastHrElCosts + previousCounter,domoticz.LOG_INFO)
	            
    	            -- if end of day, reset the cumulative daily totals 
    	            if (tonumber(os.date("%H"))==23) then
    	                domoticz.data.PreviousHrCumElCosts=domoticz.utils.round(domoticz.variables(idxDayElTransport).value+domoticz.variables(idxMonthElFixed).value/31,2)
    	                domoticz.data.PreviousHrCumElNett=0
    	            else
    	                domoticz.data.PreviousHrCumElCosts=CumElCosts
    	                domoticz.data.PreviousHrCumElNett=todayElNett
    	            end    
    	            
    	            -- first calculate cumulative daily total and last hour gas usage
    	            local todayGasNett=domoticz.devices(idxGasP1).counterToday
    	            domoticz.log('counterToday :' .. domoticz.devices(idxGasP1).counterToday,domoticz.LOG_INFO)
    	            domoticz.log('todayGasNett :' .. todayGasNett,domoticz.LOG_INFO)
    	            local lastHrGasNett = todayGasNett - domoticz.data.PreviousHrCumGasNett
    	            domoticz.log('lastHrGasNett :' .. lastHrGasNett,domoticz.LOG_INFO)
    	            -- then calculate the costs and add to device
    	            local lastHrGasCosts = lastHrGasNett * domoticz.devices(idxCurrentDynamicGasPrice).sensorValue * (100 + domoticz.variables(idxBTWpercentage).value)/100
    	            domoticz.log('lastHrGasCosts :' .. lastHrGasCosts,domoticz.LOG_INFO)
    	            local CumGasCosts = domoticz.utils.round(domoticz.data.PreviousHrCumGasCosts + lastHrGasCosts,2)
    	            domoticz.log('CumGasCosts :' .. CumGasCosts,domoticz.LOG_INFO)
    	            domoticz.devices(idxDynamicGasCosts).updateCustomSensor(CumGasCosts)
    	            
    	            previousCounter=0 
		            divider=100
		            previousCounter=domoticz.devices(idxDynamicGasCostsCntr).counter
		            --domoticz.devices(idxDynamicGasCostsCntr).updateCounter(lastHrGasCosts*divider + previousCounter*divider)
		            domoticz.devices(idxDynamicGasCostsCntr).updateCounter(CumGasCosts*divider)
		            domoticz.log('previousCounter :' .. previousCounter,domoticz.LOG_INFO)
		            domoticz.log('newcounter :' .. lastHrGasCosts + previousCounter,domoticz.LOG_INFO)
	            
    	            -- if end of day, reset the cumulative daily totals 
    	            if (tonumber(os.date("%H"))==23) then
    	                domoticz.data.PreviousHrCumGasCosts=domoticz.utils.round(domoticz.variables(idxDayGasTransport).value+domoticz.variables(idxMonthGasFixed).value/31,2)
    	                domoticz.data.PreviousHrCumGasNett=0
    	            else
    	                domoticz.data.PreviousHrCumGasCosts=CumGasCosts
    	                domoticz.data.PreviousHrCumGasNett=todayGasNett
    	            end     
    	            
    	            
	            else
        	        -- section to launch the EU electricity API get request 
        	        local UrlStart='https://web-api.tp.entsoe.eu/api?'  -- the API website
        	        local EUtoken=domoticz.variables(idxEUtoken)        -- user variable with API token
        	        local DocType='A44'                                 -- day ahead prices document type 
        	        local PriceRegion='10YNL----------L'                -- region is set to The Netherlands (adapt to your need as per API documentation)
        	        
        	        -- depending on launch hour, get current or tomorrow's data
        	        local dayahead=0
        	        if tonumber(os.date("%H"))>=23 then                 -- day ahead available after 15:00, otherwise retrieve current day, depending on the time this script is launched
        	            dayahead=24*60*60  -- seconds for full day ahead
        	        end     
        	        local PricePeriodStart=os.date("%Y%m%d",os.time()+ dayahead) .. "0000"  -- range 00:00 to 23:00, this will return full day anyway
        	        local PricePeriodEnd=os.date("%Y%m%d", os.time()+ dayahead) .. "2300"    -- depending on time the script is launched, get current day or tomorrow's data
        	        
        	        -- compose the full URL
        	        local EUurl=UrlStart .. 'securityToken=' .. EUtoken.value .. '&documentType=' .. DocType .. '&in_Domain=' .. PriceRegion .. '&out_Domain=' .. PriceRegion .. '&periodStart=' .. PricePeriodStart .. '&periodEnd=' .. PricePeriodEnd
        	        
        	        domoticz.log("URL : " .. EUurl, domoticz.LOG_INFO)
        	        
        	        -- launch the URL
        			domoticz.openURL({
        				url = EUurl,
        				method = 'GET',
        				callback = 'EUprices', -- must match httpResponses above
        			})
        			
        			-- section to launch the EnergyZero gas prices API get request (UTC timing), run between 00:00 and 01:00
        	        local GASUrlStart='https://api.energyzero.nl/v1/energyprices?'  -- the API website
        	        local usageType=2  -- GAS
        	        
        	        -- always get current day data
        	        local GasPricePeriodStart=os.date("%Y-%m-%d",os.time()) .. "T00:00:00.000Z"  -- this first price is valid from 01:00 CET
        	        local GASPricePeriodEnd=os.date("%Y-%m-%d", os.time()) .. "T23:59:59.999Z"            	        
        	        
        	        -- compose the full URL
        	        local EZurl=GASUrlStart .. 'fromDate=' .. GasPricePeriodStart .. '&tillDate=' .. GASPricePeriodEnd .. '&interval=4&usageType=' .. usageType .. '&inclBtw=false'
        	        
        	        domoticz.log("URL : " .. EZurl, domoticz.LOG_INFO)
        	        
        	        -- launch the URL
        			domoticz.openURL({
        				url = EZurl,
        				method = 'GET',
        				callback = 'EZGASprices', -- must match httpResponses above
        			})
        			
        	    end		
    		end	
		else 
		    if (item.isHTTPResponse) then
		        -- response to openURL (HTTP GET) request was received
		        if (item.trigger=="EUprices") then
    			    if (item.ok) then
    			        if (item.isXML) then   -- should be XML
    			            --domoticz.log('start dumptable', domoticz.LOG_INFO)
    			            --domoticz.utils.dumpTable(item.xml)  -- dumpTable can be used for debugging
    			            --domoticz.log('end dumptable', domoticz.LOG_INFO)
    			            
    			            ElDayPrices.reset()  -- remove historic prices from previous run
    			            
    			            for id = 1, 24 do
                                domoticz.log('adding EL price : ' .. item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'],domoticz.LOG_INFO)
                                ElDayPrices.add(item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'])
                            end

    			        else
    				        domoticz.log('No XML received', domoticz.LOG_INFO)
    				    end
    			    else
    				    domoticz.log('There was a problem handling the request. Item not ok', domoticz.LOG_INFO)
    				    domoticz.log(item, domoticz.LOG_INFO)
    			    end
	            else
	                -- trigger was not eu electricity prices but energyzero gas prices
                    domoticz.log('start gasprices', domoticz.LOG_INFO)
    	            if (item.ok) then
    	                domoticz.log('gasprices ok', domoticz.LOG_INFO)
    			        if (item.isJSON) then   -- should be JSON
    			            --domoticz.log('start dumptable', domoticz.LOG_INFO)
    			            --domoticz.utils.dumpTable(item.json)  -- dumpTable can be used for debugging
    			            --domoticz.log('end dumptable', domoticz.LOG_INFO)
    			            
    			            GasDayPrices.reset()  -- remove historic prices from previous run
    			            
    			            for gasid = 1, 24 do
    			                -- this adds prices from current day 01:00-02:00 to next day 00:00-01:00 intervals
                                domoticz.log('adding GAS price : ' .. item.json.Prices[gasid].price,domoticz.LOG_INFO)
                                GasDayPrices.add(item.json.Prices[gasid].price)
                            end
                          
    			        else
    				        domoticz.log('No JSON received', domoticz.LOG_INFO)
    				    end
    			    else
    				    domoticz.log('There was a problem handling the request. Item not ok', domoticz.LOG_INFO)
    				    domoticz.log(item, domoticz.LOG_INFO)
    			    end
			    end
		    end
	    end
	end
}

willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

Please note:
1) as highlighted on line 18 and 22 the use of counters was added for testing purposes. As it turns out, counters are not suitable for costs tracking if those costs can go down as well. See separate forum topic here:viewtopic.php?f=4&t=39476 so all lines related to those counters should be removed.
2) The script is initially designed for a Dutch situation and includes a daily transport cost and a monthly fixed costs. It is assumed that the user variables with those costs show the price including BTW(VAT).
3) The local energy tax per kWh or M3 is however not included, nor is the fixed daily tax reduction, tax ceiling or any other tax measure or compensation the government might come up with nowadays.
4) Also any additional charges from the provider, where applicable, are not included.

So please adapt the script to your own needs, taking the above into account.
sietzehh
Posts: 1
Joined: Saturday 24 December 2022 8:17
Target OS: NAS (Synology & others)
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by sietzehh »

Just decided to switch over to a flexible energy contract, your script works like a charm for current prices, building a nice graph since last night howere is there a way to present the future today and day ahead prices? Browsing the forum it seems there is curently now solution within Domoticz?
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

sietzehh wrote: Saturday 24 December 2022 8:25 Just decided to switch over to a flexible energy contract, your script works like a charm for current prices, building a nice graph since last night howere is there a way to present the future today and day ahead prices? Browsing the forum it seems there is curently now solution within Domoticz?
I have so far not found a device that can be used to load the day-ahead prices and display them in a graph. All devices are designed to record the present and then show the history in the graphs.

But maybe, if you use a managed counter and then load the prices with updateHistory() in the shortlog it might work? I have not tried it but this is the only device where you can control the entries in the shortlog (and history log) yourself. Downside is that counters in general, and also managed counters, cannot handle negative values, so negative prices (last year 70 hours) cannot be stored and/or displayed. I am also not sure what happens if you load the entries with future datetime into the shortlog. Maybe you need to use fake datetimes, like loading future 24 hr prices into the shortlog every 5 minutes with current datetime ... just guessing. Maybe I will try it one of these days.
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

sietzehh wrote: Saturday 24 December 2022 8:25 Just decided to switch over to a flexible energy contract, your script works like a charm for current prices, building a nice graph since last night howere is there a way to present the future today and day ahead prices? Browsing the forum it seems there is curently now solution within Domoticz?
I have done a test, just to see whether it is possible to make those future prices visible in a domoticz graph. Result positive, except for negative prices (as expected). Tomorrow there are 2 hours with negative price, so a good test !!!

What I did:
1) create a managed counter device of type costs, with unit euro.
2) copied my script above and stripped it from all functionality except the part to import the ENTSOE day ahead prices.
3) added an updateHistory() statement to it in order to load every price *100 onto the managed counter with a timestamp into the future (this results in an entry in the meter table).
4) checked the meter table and checked the graph of the device.

Conclusion is that it is possible to load tomorrow's prices onto the device with a future timestamp. It does result in entries in the meter table with a future timestamp and it does result in a graph showing the prices for the day. The only limitation is that negative prices cannot be loaded and shown. Negative prices result in zero. This is a known limitation of managed counters. Also note if you run the script for a second time with the same timestamp then the existing values get replaced (which is nice).

Also note you need to exclude the managed counter from the domoticz daily closing by adding "DisableLogAutoUpdate:dHJ1ZQ==" into the options field of the devicestatus table, for example using sqlite3. (see https://www.domoticz.com/wiki/Domoticz_ ... n_counters)

Since this was just a quick test, my script is a bit dirty:
1) I hardcoaded the timestamp.
2) I multiplied the prices by 100 in order not to loose the decimals, so the unit is now EURO per 100 MWh.
but as a proof-of-principle it works.

Here is the graph:
Screenshot-2022-12-27-224537.jpg
Screenshot-2022-12-27-224537.jpg (54.23 KiB) Viewed 7152 times
Here is the script:

Code: Select all

-- devices
local idxMgdCounter=112

-- user variables
local idxEUtoken=12  -- idx of the user variable with the API token


return {
	on = {
		timer = {
		    'at 22:17',  -- Timer to get new electricity prices. Adapt timer to your needs. Normally new electricty prices are available after 15:00. 
		},
		httpResponses = {
			'EUDAYprices', -- must match with the callback passed to the openURL command in the code below
		}
	},
	data =  {
            ElDayPrices               = { history = true, maxItems = 24 },
    },
	logging = {
		level = domoticz.LOG_INFO,
		marker = 'EU day ahead prices',
	},
	execute = function(domoticz, item)
	    
	    local ElDayPrices = domoticz.data.ElDayPrices

	    if (item.isTimer) then
	        
	        -- section to launch the EU electricity API get request 
	        local UrlStart='https://web-api.tp.entsoe.eu/api?'  -- the API website
	        local EUtoken=domoticz.variables(idxEUtoken)        -- user variable with API token
	        local DocType='A44'                                 -- day ahead prices document type 
	        local PriceRegion='10YNL----------L'                -- region is set to The Netherlands (adapt to your need as per API documentation)
	        
	        -- depending on launch hour, get current or tomorrow's data
	        local dayahead=0
	        if tonumber(os.date("%H"))>=22 then                 -- day ahead available after 15:00, otherwise retrieve current day, depending on the time this script is launched
	            dayahead=24*60*60  -- seconds for full day ahead
	        end     
	        local PricePeriodStart=os.date("%Y%m%d",os.time()+ dayahead) .. "0000"  -- range 00:00 to 23:00, this will return full day anyway
	        local PricePeriodEnd=os.date("%Y%m%d", os.time()+ dayahead) .. "2300"    -- depending on time the script is launched, get current day or tomorrow's data
	        
	        -- compose the full URL
	        local EUurl=UrlStart .. 'securityToken=' .. EUtoken.value .. '&documentType=' .. DocType .. '&in_Domain=' .. PriceRegion .. '&out_Domain=' .. PriceRegion .. '&periodStart=' .. PricePeriodStart .. '&periodEnd=' .. PricePeriodEnd
	        
	        domoticz.log("URL : " .. EUurl, domoticz.LOG_INFO)
	        
	        -- launch the URL
			domoticz.openURL({
				url = EUurl,
				method = 'GET',
				callback = 'EUDAYprices', -- must match httpResponses above
			})
			
    
		else 
		    if (item.isHTTPResponse) then
		        -- response to openURL (HTTP GET) request was received
		        if (item.trigger=="EUDAYprices") then
    			    if (item.ok) then
    			        if (item.isXML) then   -- should be XML
    			            --domoticz.log('start dumptable', domoticz.LOG_INFO)
    			            --domoticz.utils.dumpTable(item.xml)  -- dumpTable can be used for debugging
    			            --domoticz.log('end dumptable', domoticz.LOG_INFO)
    			            
    			            ElDayPrices.reset()  -- remove historic prices from previous run
    			            
    			            domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 00:00:00","0;0") -- add a zero start value
    			            
    			            for id = 1, 24 do
                                domoticz.log('adding EL price : ' .. item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'],domoticz.LOG_INFO)
                                ElDayPrices.add(item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'])
                                
                                local currentDateTime=os.date("%Y-%m-%d %H:00:00",os.time()+2*60*60+id*60*60)
                                local changevalue=tonumber(item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount']) * 100
                                local historystring=changevalue .. ";" .. changevalue 
                        		domoticz.log("domoticz.devices(idxMgdCounter).updateHistory(\"" .. currentDateTime .. "\",\"" .. historystring .. "\")", domoticz.LOG_INFO)
		                        domoticz.devices(idxMgdCounter).updateHistory(currentDateTime,historystring)
                                
                            end

    			        else
    				        domoticz.log('No XML received', domoticz.LOG_INFO)
    				    end
    			    else
    				    domoticz.log('There was a problem handling the request. Item not ok', domoticz.LOG_INFO)
    				    domoticz.log(item, domoticz.LOG_INFO)
    			    end
	            end
		    end
	    end
	end
}
here is the script output

Code: Select all

2022-12-27 22:17:01.136 Status: dzVents: Info: EU day ahead prices: ------ Start internal script: Script #4: HTTPResponse: "EUDAYprices"
2022-12-27 22:17:01.187 Status: dzVents: Info: EU day ahead prices: adding EL price : 40.28
2022-12-27 22:17:01.188 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 01:00:00","4028.0;4028.0")
2022-12-27 22:17:01.188 Status: dzVents: Info: EU day ahead prices: adding EL price : 33.13
2022-12-27 22:17:01.188 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 02:00:00","3313.0;3313.0")
2022-12-27 22:17:01.189 Status: dzVents: Info: EU day ahead prices: adding EL price : 9.83
2022-12-27 22:17:01.189 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 03:00:00","983.0;983.0")
2022-12-27 22:17:01.189 Status: dzVents: Info: EU day ahead prices: adding EL price : -1.35
2022-12-27 22:17:01.190 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 04:00:00","-135.0;-135.0")
2022-12-27 22:17:01.190 Status: dzVents: Info: EU day ahead prices: adding EL price : -0.65
2022-12-27 22:17:01.190 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 05:00:00","-65.0;-65.0")
2022-12-27 22:17:01.191 Status: dzVents: Info: EU day ahead prices: adding EL price : 0.78
2022-12-27 22:17:01.191 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 06:00:00","78.0;78.0")
2022-12-27 22:17:01.191 Status: dzVents: Info: EU day ahead prices: adding EL price : 17.29
2022-12-27 22:17:01.192 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 07:00:00","1729.0;1729.0")
2022-12-27 22:17:01.192 Status: dzVents: Info: EU day ahead prices: adding EL price : 69.85
2022-12-27 22:17:01.192 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 08:00:00","6985.0;6985.0")
2022-12-27 22:17:01.193 Status: dzVents: Info: EU day ahead prices: adding EL price : 110.00
2022-12-27 22:17:01.193 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 09:00:00","11000.0;11000.0")
2022-12-27 22:17:01.193 Status: dzVents: Info: EU day ahead prices: adding EL price : 120.00
2022-12-27 22:17:01.194 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 10:00:00","12000.0;12000.0")
2022-12-27 22:17:01.194 Status: dzVents: Info: EU day ahead prices: adding EL price : 118.00
2022-12-27 22:17:01.194 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 11:00:00","11800.0;11800.0")
2022-12-27 22:17:01.195 Status: dzVents: Info: EU day ahead prices: adding EL price : 120.00
2022-12-27 22:17:01.195 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 12:00:00","12000.0;12000.0")
2022-12-27 22:17:01.195 Status: dzVents: Info: EU day ahead prices: adding EL price : 120.00
2022-12-27 22:17:01.196 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 13:00:00","12000.0;12000.0")
2022-12-27 22:17:01.196 Status: dzVents: Info: EU day ahead prices: adding EL price : 100.00
2022-12-27 22:17:01.196 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 14:00:00","10000.0;10000.0")
2022-12-27 22:17:01.197 Status: dzVents: Info: EU day ahead prices: adding EL price : 74.90
2022-12-27 22:17:01.197 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 15:00:00","7490.0;7490.0")
2022-12-27 22:17:01.197 Status: dzVents: Info: EU day ahead prices: adding EL price : 100.00
2022-12-27 22:17:01.198 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 16:00:00","10000.0;10000.0")
2022-12-27 22:17:01.198 Status: dzVents: Info: EU day ahead prices: adding EL price : 114.26
2022-12-27 22:17:01.198 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 17:00:00","11426.0;11426.0")
2022-12-27 22:17:01.199 Status: dzVents: Info: EU day ahead prices: adding EL price : 121.26
2022-12-27 22:17:01.199 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 18:00:00","12126.0;12126.0")
2022-12-27 22:17:01.199 Status: dzVents: Info: EU day ahead prices: adding EL price : 119.90
2022-12-27 22:17:01.200 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 19:00:00","11990.0;11990.0")
2022-12-27 22:17:01.200 Status: dzVents: Info: EU day ahead prices: adding EL price : 95.14
2022-12-27 22:17:01.200 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 20:00:00","9514.0;9514.0")
2022-12-27 22:17:01.201 Status: dzVents: Info: EU day ahead prices: adding EL price : 91.35
2022-12-27 22:17:01.201 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 21:00:00","9135.0;9135.0")
2022-12-27 22:17:01.201 Status: dzVents: Info: EU day ahead prices: adding EL price : 68.18
2022-12-27 22:17:01.202 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 22:00:00","6818.0;6818.0")
2022-12-27 22:17:01.202 Status: dzVents: Info: EU day ahead prices: adding EL price : 42.54
2022-12-27 22:17:01.202 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-28 23:00:00","4254.0;4254.0")
2022-12-27 22:17:01.203 Status: dzVents: Info: EU day ahead prices: adding EL price : 21.48
2022-12-27 22:17:01.203 Status: dzVents: Info: EU day ahead prices: domoticz.devices(idxMgdCounter).updateHistory("2022-12-29 00:00:00","2148.0;2148.0")
2022-12-27 22:17:01.205 Status: dzVents: Info: EU day ahead prices: ------ Finished Script #4
and here are the entries from the meter table (created on the 27th):

Code: Select all

112|0|0|2022-12-28 00:00:00
112|4028|4028|2022-12-28 01:00:00
112|3313|3313|2022-12-28 02:00:00
112|983|983|2022-12-28 03:00:00
112|0|0|2022-12-28 04:00:00
112|0|0|2022-12-28 05:00:00
112|78|78|2022-12-28 06:00:00
112|1729|1729|2022-12-28 07:00:00
112|6985|6985|2022-12-28 08:00:00
112|11000|11000|2022-12-28 09:00:00
112|12000|12000|2022-12-28 10:00:00
112|11800|11800|2022-12-28 11:00:00
112|12000|12000|2022-12-28 12:00:00
112|12000|12000|2022-12-28 13:00:00
112|10000|10000|2022-12-28 14:00:00
112|7490|7490|2022-12-28 15:00:00
112|10000|10000|2022-12-28 16:00:00
112|11426|11426|2022-12-28 17:00:00
112|12126|12126|2022-12-28 18:00:00
112|11990|11990|2022-12-28 19:00:00
112|9514|9514|2022-12-28 20:00:00
112|9135|9135|2022-12-28 21:00:00
112|6818|6818|2022-12-28 22:00:00
112|4254|4254|2022-12-28 23:00:00
112|2148|2148|2022-12-29 00:00:00
JanvdW
Posts: 118
Joined: Saturday 21 December 2019 8:36
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by JanvdW »

Nicely done. Personally, I don't find the future prices in a graph that interesting: you actually want to determine what favorable hours are to switch on and/or charge devices based on these future values. If you look at such a graph, it is quite logical which times are best suited for this, but it is still quite difficult to convert that into an algorithm. Does anyone already have experience with that?
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

JanvdW wrote: Tuesday 27 December 2022 23:13 Nicely done. Personally, I don't find the future prices in a graph that interesting: you actually want to determine what favorable hours are to switch on and/or charge devices based on these future values. If you look at such a graph, it is quite logical which times are best suited for this, but it is still quite difficult to convert that into an algorithm. Does anyone already have experience with that?
Yes, it is a nice challenge:
1) First you need to know some basic variables: the total charge/discharge capacity and the charge/discharge time needed, in case of batteries.
2) You want to take a minimum for charge and maximum for discharge, maybe with extra hours before or after, depending on the charge/discharge time (if it takes more than 1 hour)
3) You want to do that multiple times per day if possible.
4) You might want to take next days data into account as soon as that is published.

In the example graph above:
1) charge the battery when prices are negative between 03:00 and 05:00. You get paid to get electricity from the grid.
2) then discharge from 9:00 to 10:00
3) charge again from 10:00 to 11:00? is it worth it? and then discharge again between 11:00 and 13:00
4) charge again from 14:00 to 15:00
5) discharge again from 17:00 to 18:00
6) even if the value is low, you probably don't want to charge already at 23:00 since probably the price goes down further the next morning. Note next day's prices are published at 15:00.

Key to a good solution is to find the local minimum and maximum prices. I think probably by checking its neighbours. Neighbours should both be higher, in case of minimum, or lower, in case of maximum. In the example you would find 3 minimum values, a 3 maximum values.
This will provide you with the pairs for charging and discharging. So, if you can charge/discharge full capacity within 1 hour it is not too difficult.

But if it takes more than 1 hour...how to optimise ... need to think about it more. Feels like a recursive algorithm might do the trick here.
JanvdW
Posts: 118
Joined: Saturday 21 December 2019 8:36
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by JanvdW »

I was still thinking about this for a while and came to the conclusion that you have to deal with two sets of variables:
  • price driven. You need to select the optimum (1, 2, 3 hour) time slots for loading/ unloading in the next x hours. Based on the device characteristics the most appropriate time slots can be selected.
  • device driven. Depending on the device characteristics you have different needs to choose optimal hours. Some examples:
    • washing machine is busy for three hours; you do not want to interrupt when it's started. So in this case you need the lowest price for a three hour time slot.
    • The battery of an electric bike is charging for 2,5 hours; it's no problem to interrupt loading for some hours. So you need three time slots (not necessarily consecutive) with the lowest price. Any timeslot before you leave next morning is appropriate.
    • home battery charging/ discharging depends on the battery capacity, % filled, the percentage you want to fill/unfill the battery (85%/15%?) and the desired difference between min/max prices (because of efficiency loss, wear). Together this defines the maximum number of time slots (not necessarily consecutive). If no suitable time slots are available, there is no need to charge.
I think that the best solution is to run a script every x hours that selects the optimum timeslots for these scenarios (both for load and unload) and store them in (global user) data, e.g.:
  • 3 or 4 best slots for 1 hour
  • 1 or 2 best slots for 2 hours
  • 1 best slot for 3 hours
Other scripts can select the optimal available time slots to control the devices.

It's quite easy to select a time slot with the lowest price in a certain period, e.g. with code like this (note: it's important that the original script loads data from the next day short after 15:00; otherwise you there's not enough data in the data set)

Code: Select all

lowPriceIndex = VarIndex 			-- Starting point current time
local NrAvailablePrices = 9					-- Timeslot to find lowest price
for id = 1, NrAvailablePrices do				-- Determine lowest values in the next hours 
	VarIndexStroom = VarIndexStroom - 1
	domoticz.log('Price electricity (' .. tonumber(ElDayPrices.get(VarIndexStroom).data) .. ')',domoticz.LOG_INFO)
	if tonumber(ElDayPrices.get(lowPriceIndex).data) > tonumber(ElDayPrices.get(VarIndexStroom).data) then
		lowPriceIndex = VarIndex
	end
end
domoticz.data.lowPriceIndex = lowPriceIndex
domoticz.log('Lowest price for electricity is at ' .. 24-lowPriceIndex .. ':00 uur (' ..tonumber(ElDayPrices.get(lowPriceIndex).data) .. ')',domoticz.LOG_INFO)
This can be extended for consecutive timeslots with the lowest price. The point is that there are quite a lot scenario's, which makes it complex.
JanvdW
Posts: 118
Joined: Saturday 21 December 2019 8:36
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by JanvdW »

Additionally, when the dataset for the next day is loaded between 15:00 and 16:00 the number of records in the dataset varies (24 resp. 48 records).

You can handle this bij creating a separate VarIndex for Electricity (because it's different from Gas) and define the number of records depending on time:

Code: Select all

local NrRecordsEl  = 48
if (tonumber(os.date("%H")) * 60 + tonumber(os.date("%M"))) < 945 then
	NrRecordsEl    = 24
end
local VarIndexEl        = NrRecordsStroom-tonumber(os.date("%H"))
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

Best to move further exchange about optimisation during the day to a separate thread, thanks.
tomipaci
Posts: 1
Joined: Tuesday 03 January 2023 8:00
Target OS: NAS (Synology & others)
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by tomipaci »

good day,

I tried to run your script. It looks great, but I don't have a lot of experience, so I would like to ask for precise instructions on creating the device. I don't know what's wrong with me.
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

tomipaci wrote: Tuesday 03 January 2023 8:05 good day,

I tried to run your script. It looks great, but I don't have a lot of experience, so I would like to ask for precise instructions on creating the device. I don't know what's wrong with me.
Menu: setup, hardware, choose type dummy and fill in the rest of the fields.
On the same page, on the hardware line, click on "create virtual sensor", choose type "custom sensor" and fill in the fields.
Then setup, more options, user variables.
Note down all idx numbers and replace them in the script.
Read the documentation in the script and in the wiki where needed .......
JanvdW
Posts: 118
Joined: Saturday 21 December 2019 8:36
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by JanvdW »

Now and then (today amongst others) I see an error message every hour at *.45:

Code: Select all

attempt to index a nil value (field '?')
This error is caused by the command that aims to write the gas prices to the Domoticz log (row 240 in the script).

The persistent data contain the gas prices that were stored yesterday evening. It turns out that the http request returns five values (instead of 24). I am not sure, but when I remember well, this was the same last weekend. Possibly no one manages this source during the weekends and public holidays?
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

Yes, I have the same problem. Clearly an unreliable source for gas prices.

I think I also need to improve the script to handle the error better.
renerene
Posts: 316
Joined: Wednesday 03 August 2016 11:36
Target OS: -
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by renerene »

willemd wrote: Tuesday 27 December 2022 23:47 Key to a good solution is to find the local minimum and maximum prices.
ChatGpt to the rescue:

Image

Code: Select all

def find_subarray(A, X):
    n = len(A)
    min_sum = float('inf')
    j_index, k_index = 0, 0
    for i in range(n - X + 1):
        subarray_sum = sum(A[i:i+X])
        if subarray_sum < min_sum:
            min_sum = subarray_sum
            j_index = i
            k_index = i + X - 1
    return j_index, k_index
JanvdW
Posts: 118
Joined: Saturday 21 December 2019 8:36
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by JanvdW »

Great; ChatGPT for president :-). Asking for this function in dzVents code returns this:

Code: Select all

return {
	on = {
		customEvents = {
			'CalculateJK'
		}
	},
	execute = function(domoticz, item)
		local array = item.array
		local x = item.x
		local j, k
		local min = math.huge
		
		for i = 1, #array - x + 1 do
			local currentSum = 0
			
			for j = i, i + x - 1 do
				currentSum = currentSum + array[j]
			end
			
			if currentSum < min then
				min = currentSum
				j = i
				k = i + x - 1
			end
		end
		
		domoticz.log("J = " .. j .. ", K = " .. k)
	end
}
willemd
Posts: 621
Joined: Saturday 21 September 2019 17:55
Target OS: Raspberry Pi / ODroid
Domoticz version: 2024.1
Location: The Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by willemd »

Stop hyjacking and posting nonsense to this thread please. This thread was to share experience with the loading of the hourly electricity and daily gas prices for the day ahead for anyone to re-use.

Optimisation of usage of price variation is a total different story and those chatGPT programs posted here only form a beginning of a solution, a tiny fraction. Finding a minimum and maximum is easy but then ....

As posted in a separate thread, I have created a python program that plans for battery charging and discharging using those minimum and maximum values, but also taking into account battery capacity, charging and discharging speed, solar panel production etc. Until you try it for yourself you have no idea of the complexity.....
https://github.com/WillemD61/battery-planning
Alain
Posts: 164
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by Alain »

JanvdW wrote: Wednesday 28 December 2022 23:33 I was still thinking about this for a while and came to the conclusion that you have to deal with two sets of variables:
  • price driven. You need to select the optimum (1, 2, 3 hour) time slots for loading/ unloading in the next x hours. Based on the device characteristics the most appropriate time slots can be selected.
  • device driven. Depending on the device characteristics you have different needs to choose optimal hours. Some examples:
    • washing machine is busy for three hours; you do not want to interrupt when it's started. So in this case you need the lowest price for a three hour time slot.
    • The battery of an electric bike is charging for 2,5 hours; it's no problem to interrupt loading for some hours. So you need three time slots (not necessarily consecutive) with the lowest price. Any timeslot before you leave next morning is appropriate.
    • home battery charging/ discharging depends on the battery capacity, % filled, the percentage you want to fill/unfill the battery (85%/15%?) and the desired difference between min/max prices (because of efficiency loss, wear). Together this defines the maximum number of time slots (not necessarily consecutive). If no suitable time slots are available, there is no need to charge.
I think that the best solution is to run a script every x hours that selects the optimum timeslots for these scenarios (both for load and unload) and store them in (global user) data, e.g.:
  • 3 or 4 best slots for 1 hour
  • 1 or 2 best slots for 2 hours
  • 1 best slot for 3 hours
Other scripts can select the optimal available time slots to control the devices.

It's quite easy to select a time slot with the lowest price in a certain period, e.g. with code like this (note: it's important that the original script loads data from the next day short after 15:00; otherwise you there's not enough data in the data set)

Code: Select all

lowPriceIndex = VarIndex 			-- Starting point current time
local NrAvailablePrices = 9					-- Timeslot to find lowest price
for id = 1, NrAvailablePrices do				-- Determine lowest values in the next hours 
	VarIndexStroom = VarIndexStroom - 1
	domoticz.log('Price electricity (' .. tonumber(ElDayPrices.get(VarIndexStroom).data) .. ')',domoticz.LOG_INFO)
	if tonumber(ElDayPrices.get(lowPriceIndex).data) > tonumber(ElDayPrices.get(VarIndexStroom).data) then
		lowPriceIndex = VarIndex
	end
end
domoticz.data.lowPriceIndex = lowPriceIndex
domoticz.log('Lowest price for electricity is at ' .. 24-lowPriceIndex .. ':00 uur (' ..tonumber(ElDayPrices.get(lowPriceIndex).data) .. ')',domoticz.LOG_INFO)
This can be extended for consecutive timeslots with the lowest price. The point is that there are quite a lot scenario's, which makes it complex.
I've tried adding this code to the code above to find the lowest price in the day ahead table, but I get an error saying that I am trying to do arithmetic on a nil value (global variable VarIndexStroom). I'm not completely understanding how to work with putting data in some sort of array (which I can't see) and then working with that. I have always just used user variables and am new to this approach. I hope someone can help me. I would like to identify both the lowest price and the highest price, but more importantly, at which time of day they occur.
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
JanvdW
Posts: 118
Joined: Saturday 21 December 2019 8:36
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: script to load ENTSOE day ahead electricity prices

Post by JanvdW »

Alain wrote: Saturday 15 April 2023 5:08 I've tried adding this code to the code above to find the lowest price in the day ahead table, but I get an error saying that I am trying to do arithmetic on a nil value (global variable VarIndexStroom). I'm not completely understanding how to work with putting data in some sort of array (which I can't see) and then working with that. I have always just used user variables and am new to this approach. I hope someone can help me. I would like to identify both the lowest price and the highest price, but more importantly, at which time of day they occur.
This has nothing to do with the global variable. By the way, you can find the variables in the domoticz/scripts/dzVents/data folder.

Your problem has probably to do with the number of items that's available in the dataset. The loop goes outside the available range at some times.

I recognize the problem and have made quite a few changes to my script. Here's my current script for inspiration. Not completely finished yet, but hopefully this will help out.

Code: Select all

-- Script to load day-ahead electricity and gas prices into Domoticz historic variables, as a base for further processing.
-- New electricity prices are available every day at 13:00 for the coming 24 hrs. These are base prices. Providers of dynamic electricity contracts will add their surcharges and government taxes to it.
-- API documentation at: https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html
--    1) Register for an account at https://transparency.entsoe.eu/dashboard/show and send an email with subject "Restful API access" and your account email address in the body to [email protected]
--    2) After receipt of their confirmation, go into your account and generate your token.
--    3) Update variables and idx's on the lines below (including EUtoken which you just generated!)

-- Device idx's
local idxStroomP1                   = <idx>
local idxGasP1                      = <idx>
local idxSolar                      = <idx>
local idxCurrentDynamicStroomPrice  = <idx>
local idxDynamicStroomCosts         = <idx>
local idxDynamicSolarRevenue        = <idx>
local idxCurrentDynamicGasPrice     = <idx>
local idxDynamicGasCosts            = <idx>
local idxEnergyCosts                = <idx>
local idxVirtualEnergyCosts         = <idx>
local idxlowPriceIndex1             = <idx>
local idxlowPriceIndex2             = <idx>
local idxlowPriceIndex3             = <idx>
local idxlowPriceIndex4             = <idx>

-- variables for EU market prices gas and electricity
local UrlStart                      = 'https://web-api.tp.entsoe.eu/api?'           -- EU electricity API - launch the EU electricity API get request
local DocType                       = 'A44'                                         -- EU electricity API - day ahead prices document type 
local PriceRegion                   = '10YNL----------L'                            -- EU electricity API - region is set to The Netherlands (adapt to your need as per API documentation)
local EUtoken                       = 'f9eb2b55-708b-4464-ae62-6d1185435b44'        -- EU electricity API - API token
local GASUrlStart                   = 'https://api.energyzero.nl/v1/energyprices?'  -- Gas API website
local usageType                     = 2                                             -- Gas

return {
	on = {  timer = {   'at *:45',                                                  -- Get new price arrays. Prices received are in UTC so 1 hour shift needed
		                                                                            -- Prices are re-loaded every hour, to avoid using old prices when there is one error loading once per day.
		                'at *:55',                                                  -- Determine cheapest hour frames and calculate the costs of the past hour (missing last minutes, added to next hour, except end of day)
		                'at *:59' },                                                -- Store cost/revenue to corresponding devices, update current prices and set switches on/off

		    httpResponses = {   'EUprices',                                         -- must match with the callback passed to the openURL command in the code below
			                    'EZGASprices', } },
			                
	data =  {   StroomHourPrices            = { history = true, maxItems = 48 },
                NrRecordsStroom             = { initial = 24},
                GasDayPrices                = { history = true, maxItems = 24 },
                NrRecordsGas                = { initial = 24},
                PreviousHrCumStroomCosts    = { initial = 0},
                PreviousHrCumStroomNett     = { initial = 0},
                PreviousHrCumSolarRevenue   = { initial = 0},
                PreviousHrCumSolarNett      = { initial = 0},
                PreviousHrCumGasCosts       = { initial = 0},
                PreviousHrCumGasNett        = { initial = 0},
                lowPriceIndex1              = { initial = 0},
                lowPriceIndex2              = { initial = 0},
                lowPriceIndex3              = { initial = 0},
                lowPriceIndex4              = { initial = 0},
                SwitchOnTime1               = { initial = 0},
                SwitchOnTime2               = { initial = 0},
                SwitchOnTime3               = { initial = 0},
                SwitchOnTime4               = { initial = 0},
                },

    logging = { level   = domoticz.LOG_INFO,
		        marker  = 'EU and EZ day ahead prices', },

	execute = function(domoticz, item)

        -- Variables
        local BTW                           = 21                                            -- BTW percentage
        local DayStroomFixed                = 0                                             -- Fixed daily costs electrcity (excl. BTW)
        local MonthStroomFixed              = -11.52892562                                  -- Fixed monthly costs electricity (excl. BTW)
        local kwhStroom                     = 0.128471074                                   -- Variable costs electricity (per KwH), on top of EU market price (excl. BTW) -> (0,128471074 + 0,003)/1,21
        local DayGasFixed                   = 0                                             -- Fixed daily costs gas (excl. BTW)
        local MonthGasFixed                 = 19.4214876                                    -- Fixed monthly costs gas (excl. BTW)
        local m3Gas                         = 0.531123967                                   -- Variable costs gas (per m3), on top of EU market price (excl. BTW)
        local Time                          = require('Time')
        local ContractEindDatum             = Time('2023-2-28 23:59:59')                    -- Einddatum huidige energiecontract
        if domoticz.time.compare(ContractEindDatum).compare < 0 then                        -- Tarieven na ContractEindDatum bij contractwijziging
            BTW                             = 21                                            -- BTW percentage
            DayStroomFixed                  = 0                                             -- Fixed daily costs electrcity (excl. BTW)
            MonthStroomFixed                = -11.52892562                                  -- Fixed monthly costs electricity (excl. BTW)
            kwhStroom                       = 0.128471074                                   -- Variable costs electricity (per KwH), on top of EU market price (excl. BTW)
            DayGasFixed                     = 0                                             -- Fixed daily costs gas (excl. BTW)
            MonthGasFixed                   = 19.4214876                                    -- Fixed monthly costs gas (excl. BTW)
            m3Gas                           = 0.531123967                                   -- Variable costs gas (per m3), on top of EU market price (excl. BTW)
        end

	    local StroomHourPrices      = domoticz.data.StroomHourPrices
	    local GasDayPrices          = domoticz.data.GasDayPrices
	    local todayStroom           = domoticz.devices(idxStroomP1).counterToday
	    local todayStroomreturn     = domoticz.devices(idxStroomP1).counterDeliveredToday
	    local todayGas              = domoticz.devices(idxGasP1).counterToday
	    local todaySolar            = domoticz.devices(idxSolar).counterToday
        local NrRecordsStroom       = domoticz.data.NrRecordsStroom
        local VarIndexStroom        = NrRecordsStroom-tonumber(os.date("%H"))                                                                   -- last price of day is index 1
        local NrRecordsGas          = domoticz.data.NrRecordsGas
        local VarIndexGas           = NrRecordsGas-tonumber(os.date("%H"))
    
	    if (item.isTimer) then
	        
 	        if (item.trigger == 'at *:59') then

                if (tonumber(os.date("%H")) == 23) then
                    domoticz.devices(idxDynamicStroomCosts).updateCustomSensor(domoticz.data.PreviousHrCumStroomCosts)
                    domoticz.devices(idxDynamicSolarRevenue).updateCustomSensor(domoticz.data.PreviousHrCumSolarRevenue)
                    domoticz.devices(idxDynamicGasCosts).updateCustomSensor(domoticz.data.PreviousHrCumGasCosts)
                    domoticz.devices(idxEnergyCosts).updateCustomSensor(domoticz.data.PreviousHrCumStroomCosts + domoticz.data.PreviousHrCumGasCosts)
                    domoticz.devices(idxVirtualEnergyCosts).updateCustomSensor(domoticz.data.PreviousHrCumStroomCosts + domoticz.data.PreviousHrCumGasCosts)
                end
	                                                                                                                                            -- copy dynamic price to device holding the current price
	            local CurrentStroomPrice=tonumber(StroomHourPrices.get(VarIndexStroom - 1).data)/1000 + kwhStroom                               -- from EURO/MWh to EURO/kWh + (supplier) price per KwH
                local CurrentStroomPriceBTW = CurrentStroomPrice * (100 + BTW)/100
                domoticz.log('Current Dynamic Stroom price Euro/kWh: ' .. CurrentStroomPriceBTW,domoticz.LOG_INFO)
	            domoticz.devices(idxCurrentDynamicStroomPrice).updateCustomSensor(CurrentStroomPriceBTW)
	            
	            if VarIndexGas < 3  then
	                VarIndexGas = 1
	            else
	                VarIndexGas = VarIndexGas - 2                                                                                               -- -1 (dataset starts at 23:00) -1 (script runs 1 minute before :00)
	            end

	            local CurrentGasPrice=tonumber(GasDayPrices.get(VarIndexGas).data) + m3Gas                                                      -- Euro per m3, last price is for 00:00 to 01:00 next day.
	            local CurrentGasPriceBTW = CurrentGasPrice * (100 + BTW)/100
	            domoticz.log('Current Dynamic Gas price Euro/m3: ' .. CurrentGasPriceBTW,domoticz.LOG_INFO)
	            domoticz.devices(idxCurrentDynamicGasPrice).updateCustomSensor(CurrentGasPriceBTW)

                --if tonumber(os.date("%H")) < 9 or tonumber(os.date("%H")) > 19 then
                    --domoticz.notify('VarIndexGas','VarIndexGas: ' .. VarIndexGas .. ' (' .. NrRecordsGas .. ' records)',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    --domoticz.notify('Dynamic Gas price','Gas price: ' .. CurrentGasPriceBTW,domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                --end

                if tonumber(os.date("%H")) == NrRecordsStroom-1-domoticz.data.lowPriceIndex1 then                                               -- Set switches with 1 hour frame on
                    --domoticz.notify('Goedkoopste tijdslot','Start goedkoopste tijdslot van 1 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex1 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur aan (1 uur frame)',domoticz.LOG_INFO)
                    domoticz.data.SwitchOnTime1 = tonumber(os.date("%H"))
                    --domoticz.devices('Name or idx switch').switchOn().checkFirst()
                end
                if tonumber(os.date("%H")) == NrRecordsStroom-1-domoticz.data.lowPriceIndex2 then                                               -- Set switches with 2 hour frame on
                    --domoticz.notify('Goedkoopste tijdslot','Start goedkoopste tijdslot van 2 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex2 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur aan (2 uurs frame)',domoticz.LOG_INFO)
                    domoticz.data.SwitchOnTime2 = tonumber(os.date("%H"))
                end
                if tonumber(os.date("%H")) == NrRecordsStroom-1-domoticz.data.lowPriceIndex3 then                                               -- Set switches with 3 hour frame on
                    --domoticz.notify('Goedkoopste tijdslot','Start goedkoopste tijdslot van 3 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex3 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur aan (3 uurs frame)',domoticz.LOG_INFO)
                    domoticz.data.SwitchOnTime3 = tonumber(os.date("%H"))
                end
                if tonumber(os.date("%H")) == NrRecordsStroom-1-domoticz.data.lowPriceIndex4 then                                               -- Set switches with 4 hour frame on
                    --domoticz.notify('Goedkoopste tijdslot','Start goedkoopste tijdslot van 4 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex4 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur aan (4 uurs frame)',domoticz.LOG_INFO)
                    domoticz.data.SwitchOnTime4 = tonumber(os.date("%H"))
                end

                if tonumber(os.date("%H")) == domoticz.data.SwitchOnTime1 + 1 then                                                              -- Set switches with 1 hour frame off
                    --domoticz.notify('Goedkoopste tijdslot','Eind goedkoopste tijdslot van 1 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex1 + 1 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur uit (1 uur frame)',domoticz.LOG_INFO)
                    --domoticz.devices('Name or idx switch').switchOff().checkFirst()
                end
                if tonumber(os.date("%H")) == domoticz.data.SwitchOnTime2 + 2 then                                                              -- Set switches with 2 hour frame off
                    --domoticz.notify('Goedkoopste tijdslot','Eind goedkoopste tijdslot van 2 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex2 + 2 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur uit (2 uurs frame)',domoticz.LOG_INFO)
                end
                if tonumber(os.date("%H")) == domoticz.data.SwitchOnTime3 + 3 then                                                              -- Set switches with 3 hour frame off
                    --domoticz.notify('Goedkoopste tijdslot','Eind goedkoopste tijdslot van 3 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex3 + 3 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur uit (3 uurs frame)',domoticz.LOG_INFO)
                end
                if tonumber(os.date("%H")) == domoticz.data.SwitchOnTime4 + 4 then                                                              -- Set switches with 4 hour frame off
                    --domoticz.notify('Goedkoopste tijdslot','Eind goedkoopste tijdslot van 4 uur (index ' .. NrRecordsStroom-1-domoticz.data.lowPriceIndex4 + 4 .. ')',domoticz.PRIORITY_NORMAL,nil,nil,domoticz.NSS_TELEGRAM)
                    domoticz.log('Zet schakelaars voor laden apparatuur uit (4 uurs frame)',domoticz.LOG_INFO)
                end

	        else
	            if (item.trigger == 'at *:55') then                                                                                             -- first calculate cumulative daily total and last hour electricity usage
    	            local todayStroomNett=domoticz.devices(idxStroomP1).counterToday-domoticz.devices(idxStroomP1).counterDeliveredToday
    	            local lastHrStroomNett = todayStroomNett - domoticz.data.PreviousHrCumStroomNett
                    local lastHrStroomCosts = lastHrStroomNett * domoticz.devices(idxCurrentDynamicStroomPrice).sensorValue * (100 + BTW)/100   -- then calculate the costs and add to device
    	            local CumStroomCosts = domoticz.utils.round(domoticz.data.PreviousHrCumStroomCosts + lastHrStroomCosts,2)
    	            domoticz.devices(idxDynamicStroomCosts).updateCustomSensor(CumStroomCosts)
    	                                                                                                                                       domoticz.log('todayStroomNett: ' .. domoticz.devices(idxStroomP1).counterToday-domoticz.devices(idxStroomP1).counterDeliveredToday,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('counterToday: ' .. domoticz.devices(idxStroomP1).counterToday,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('counterDeliveredToday: ' .. domoticz.devices(idxStroomP1).counterDeliveredToday,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('lastHrStroomNett: ' .. lastHrStroomNett,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('lastHrStroomCosts: ' .. lastHrStroomCosts,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('CumStroomCosts: ' .. CumStroomCosts,domoticz.LOG_INFO)
    	            if (tonumber(os.date("%H"))==23) then                                                                                       -- if end of day, reset the cumulative daily totals 
    	                domoticz.data.PreviousHrCumStroomCosts = (DayStroomFixed+MonthStroomFixed/31) * (100 + BTW)/100
    	                domoticz.data.PreviousHrCumStroomNett = 0
    	            else
    	                domoticz.data.PreviousHrCumStroomCosts = CumStroomCosts
    	                domoticz.data.PreviousHrCumStroomNett = todayStroomNett
    	            end
    	            
    	            local todaySolarNett=domoticz.devices(idxSolar).counterToday
    	            local lastHrSolarNett = todaySolarNett - domoticz.data.PreviousHrCumSolarNett
                    local lastHrSolarRevenue = lastHrSolarNett * domoticz.devices(idxCurrentDynamicStroomPrice).sensorValue * (100 + BTW)/100   -- then calculate the costs and add to device
    	            local CumSolarRevenue = domoticz.utils.round(domoticz.data.PreviousHrCumSolarRevenue + lastHrSolarRevenue,2)
    	            domoticz.devices(idxDynamicSolarRevenue).updateCustomSensor(CumSolarRevenue)
    	                                                                                                                                       domoticz.log('counterToday Solar: ' .. domoticz.devices(idxSolar).counterToday,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('lastHrSolarNett: ' .. lastHrSolarNett,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('lastHrSolarRevenue: ' .. lastHrSolarRevenue,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('CumSolarRevenue: ' .. CumSolarRevenue,domoticz.LOG_INFO)
    	            if (tonumber(os.date("%H"))==23) then                                                                                       -- if end of day, reset the cumulative daily totals 
    	                domoticz.data.PreviousHrCumSolarRevenue = 0
    	                domoticz.data.PreviousHrCumSolarNett = 0
    	            else
    	                domoticz.data.PreviousHrCumSolarRevenue = CumSolarRevenue
    	                domoticz.data.PreviousHrCumSolarNett = todaySolarNett
    	            end   
    	            
    	            local todayGasNett=domoticz.devices(idxGasP1).counterToday    	                                                            -- calculate cumulative daily total and last hour gas usage
    	            local lastHrGasNett = todayGasNett - domoticz.data.PreviousHrCumGasNett
    	            local lastHrGasCosts = lastHrGasNett * domoticz.devices(idxCurrentDynamicGasPrice).sensorValue * (100 + BTW)/100            -- then calculate the costs and add to device
    	            local CumGasCosts = domoticz.utils.round(domoticz.data.PreviousHrCumGasCosts + lastHrGasCosts,2)
    	            domoticz.devices(idxDynamicGasCosts).updateCustomSensor(CumGasCosts)
    	                                                                                                                                       domoticz.log('counterToday todayGasNett: ' .. domoticz.devices(idxGasP1).counterToday,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('lastHrGasNett: ' .. lastHrGasNett,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('lastHrGasCosts: ' .. lastHrGasCosts,domoticz.LOG_INFO)
    	                                                                                                                                       domoticz.log('CumGasCosts: ' .. CumGasCosts,domoticz.LOG_INFO)
    	            if (tonumber(os.date("%H"))==23) then                                                                                       -- if end of day, reset the cumulative daily totals
    	                domoticz.data.PreviousHrCumGasCosts = (DayGasFixed+MonthGasFixed/31) * (100 + BTW)/100
    	                domoticz.data.PreviousHrCumGasNett = 0
    	            else
    	                domoticz.data.PreviousHrCumGasCosts = CumGasCosts
    	                domoticz.data.PreviousHrCumGasNett = todayGasNett
    	            end
                    
	                local TotalCosts = tonumber(domoticz.utils.round((CumStroomCosts + CumGasCosts),2))
                    local TotalUse = tonumber(domoticz.utils.round((TotalCosts + CumSolarRevenue),2))
    	            domoticz.devices(idxEnergyCosts).updateCustomSensor(TotalCosts)
                    domoticz.devices(idxVirtualEnergyCosts).updateCustomSensor(TotalUse)
                                                                                                                                               domoticz.log('TotalCosts: ' .. TotalCosts,domoticz.LOG_INFO)
                                                                                                                                               domoticz.log('TotalUse: ' .. TotalUse,domoticz.LOG_INFO)

                                                                                                                                                -- Determine cheapest time frames in the next period
            	    local IndexStart, TimeStart, IndexEnd, TimeEnd, min, TimeFrame, currentSum
            	    local NrAvailablePrices = 8                                                                                                 -- # available records: max 10. Or # available records - records in the past - max Timeframe (4)
            	    if NrRecordsStroom - tonumber(os.date("%H")) - 4 - 1 < 10 then
            	        NrAvailablePrices = NrRecordsStroom - tonumber(os.date("%H")) - 4
            	    end
            	    for TimeFrame = 1, 4 do
            	        min = math.huge                                                                                                         -- starting point for minimum value (infinity)
            	        IndexStart = 0                                                                                                          -- index values for start timeframe with lowest price
            	        IndexEnd = 0                                                                                                            -- index values for end timeframe with lowest price
            	       
                		for i = VarIndexStroom - NrAvailablePrices -1, VarIndexStroom - TimeFrame do
                		    currentSum = 0                                                                                                      -- temporary value for calculation lowest prices in time frames
                		    for IndexEnd = i, i + TimeFrame - 1 do
                			    currentSum = currentSum + tonumber(StroomHourPrices.get(IndexEnd).data)
                			end
                			if currentSum <= min then
                				min = currentSum
                				IndexEnd = i
                				IndexStart = i + TimeFrame - 1
                			end
            		    end
            		
                        if 24-IndexStart < 0 then
                            TimeStart = 48-IndexStart
                        else
                            TimeStart = 24-IndexStart
                        end
                        if 24-IndexEnd < 0 then
                            TimeEnd = 48-IndexEnd
                        else
                            TimeEnd = 24-IndexEnd
                        end
                        domoticz.log('Cheapest ' .. TimeFrame .. ' hour frame ' .. TimeStart .. ':00 uur (' ..tonumber(StroomHourPrices.get(IndexStart).data) .. ') tot  ' .. TimeEnd .. ':59 uur (' ..tonumber(StroomHourPrices.get(IndexEnd).data) .. ')',domoticz.LOG_INFO)
                        if TimeFrame == 1 then
                            domoticz.data.lowPriceIndex1 = IndexStart
                            domoticz.devices(idxlowPriceIndex1).updateCustomSensor(TimeStart)
                        elseif TimeFrame == 2 then
                            domoticz.data.lowPriceIndex2 = IndexStart
                            domoticz.devices(idxlowPriceIndex2).updateCustomSensor(TimeStart)
                        elseif TimeFrame == 3 then
                            domoticz.data.lowPriceIndex3 = IndexStart
                            domoticz.devices(idxlowPriceIndex3).updateCustomSensor(TimeStart)
                        elseif TimeFrame == 4 then
                            domoticz.data.lowPriceIndex4 = IndexStart
                            domoticz.devices(idxlowPriceIndex4).updateCustomSensor(TimeStart)
                        end
                    end

	            else                                                                                                                            -- at *:45: depending on launch hour, get current or tomorrow's data
        	        local PricePeriodStart=os.date("%Y%m%d",os.time()) .. "0000"                                                                -- range 00:00 to 23:00, this will return full day anyway
        	        local PricePeriodEnd=os.date("%Y%m%d", os.time() + 24*60*60) .. "2300"                                                      -- depending on time the script is launched, get current day and/or tomorrow's data
        	        local EUurl=UrlStart .. 'securityToken=' .. EUtoken .. '&documentType=' .. DocType .. '&in_Domain=' .. PriceRegion .. '&out_Domain=' .. PriceRegion .. '&periodStart=' .. PricePeriodStart .. '&periodEnd=' .. PricePeriodEnd
                    domoticz.log("URL : " .. EUurl, domoticz.LOG_INFO)
        			domoticz.openURL({  url         = EUurl,                                                                                    -- launch url
        				                method      = 'GET',
        				                callback    = 'EUprices', })                                                                            -- must match httpResponses above
        			                                                                                                                            -- section to launch the EnergyZero gas prices API get request (UTC timing), run between 00:00 and 01:00
        	        local GasPricePeriodStart=os.date("%Y-%m-%d",os.time()) .. "T00:00:00.000Z"                                                 -- always get current day data. This first price is valid from 01:00 CET
        	        local GASPricePeriodEnd=os.date("%Y-%m-%d", os.time()) .. "T23:59:59.999Z"            	        
        	        local EZurl=GASUrlStart .. 'fromDate=' .. GasPricePeriodStart .. '&tillDate=' .. GASPricePeriodEnd .. '&interval=4&usageType=' .. usageType .. '&inclBtw=false'
                    domoticz.log("URL : " .. EZurl, domoticz.LOG_INFO)
        			domoticz.openURL({  url         = EZurl,                                                                                    -- launch url
        				                method      = 'GET',
        				                callback    = 'EZGASprices', })                                                                         -- must match httpResponses above
        	    end		
    		end	
		else 
		    if (item.isHTTPResponse) then                                                                                                       -- response to openURL (HTTP GET) request was received
		        if (item.trigger=="EUprices") then
    			    if (item.ok) then
    			        if (item.isXML) then                                                                                                    -- should be XML
                            StroomHourPrices.reset()                                                                                            -- remove historic prices from previous run

                            if #item.xml.Publication_MarketDocument.TimeSeries == 2 then
                                domoticz.data.NrRecordsStroom = 48
                                for TS = 1, 2 do
                                    for id = 1, 24 do
                                        if TS == 1 then
                                            domoticz.log('Stroom marktprijs vandaag vanaf ' .. id-1 .. ':00 uur: ' .. item.xml.Publication_MarketDocument.TimeSeries[TS].Period.Point[id]['price.amount']/1000 .. " (" .. domoticz.utils.round((item.xml.Publication_MarketDocument.TimeSeries[TS].Period.Point[id]['price.amount']/1000 + kwhStroom) * (100 + BTW) / 100,4) .. " incl.)",domoticz.LOG_INFO)
                                        else
                                            domoticz.log('Stroom marktprijs morgen vanaf ' .. id-1 .. ':00 uur: ' .. item.xml.Publication_MarketDocument.TimeSeries[TS].Period.Point[id]['price.amount']/1000  .. " (" .. domoticz.utils.round((item.xml.Publication_MarketDocument.TimeSeries[TS].Period.Point[id]['price.amount']/1000 + kwhStroom) * (100 + BTW) / 100,4) .. " incl.)",domoticz.LOG_INFO)
                                        end
                                        StroomHourPrices.add(item.xml.Publication_MarketDocument.TimeSeries[TS].Period.Point[id]['price.amount'])
                                    end
                                end
                            else
                                domoticz.data.NrRecordsStroom = 24
                                for id = 1, 24 do
                                    domoticz.log('Stroom marktprijs vanaf ' .. id-1 .. ':00 uur: ' .. item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount']/1000  .. " (" .. domoticz.utils.round((item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount']/1000 + kwhStroom) * (100 + BTW) / 100,4) .. " incl.)",domoticz.LOG_INFO)
                                    StroomHourPrices.add(item.xml.Publication_MarketDocument.TimeSeries.Period.Point[id]['price.amount'])
                                end
                            end
                        else
                            domoticz.log('No XML received', domoticz.LOG_INFO)
    				    end
    			    else
    				    domoticz.log('There was a problem handling the request. Item not ok', domoticz.LOG_INFO)
    				    if item.statusCode == 503 then
    				        domoticz.log('Electriciteitsprijzen tijdelijk niet beschikbaar ivm onderhoud. HTTP statuscode: ' .. item.statusCode .. ' - ' .. item.statusText, domoticz.LOG_INFO)
    				    else
    				        domoticz.log('Foutsituatie. HTTP statuscode: ' .. item.statusCode .. ' - ' .. item.statusText, domoticz.LOG_INFO)
                        end
    			    end
	            else                                                                                                                            -- trigger was not EU electricity prices but energyzero gas prices
    	            if (item.ok) then
    			        if (item.isJSON) then                                                                                                   -- should be JSON
    			            GasDayPrices.reset()                                                                                                -- remove historic prices from previous run
                            domoticz.data.NrRecordsGas = #item.json.Prices
    			            for gasid = 1, domoticz.data.NrRecordsGas do                                                                        -- add prices from current day 01:00-02:00 to next day 00:00-01:00 intervals
                                domoticz.log('Gas marktprijs vandaag vanaf: ' .. gasid .. ':00 uur: ' .. item.json.Prices[gasid].price .. " (" .. domoticz.utils.round((item.json.Prices[gasid].price + m3Gas) * (100 + BTW) / 100,4) .. " incl.)",domoticz.LOG_INFO)
                                GasDayPrices.add(item.json.Prices[gasid].price)
                            end
    			        else
                            domoticz.log('No JSON received', domoticz.LOG_INFO)
    				    end
    			    else
    				    domoticz.log('There was a problem handling the request. Returned XML item is not ok.', domoticz.LOG_INFO)
    				    if item.statusCode == 503 then
    				        domoticz.log('Gasprijzen tijdelijk niet beschikbaar ivm onderhoud. HTTP statuscode: ' .. item.statusCode .. ' - ' .. item.statusText, domoticz.LOG_INFO)
    				    else
    				        domoticz.log('Foutsituatie. HTTP statuscode: ' .. item.statusCode .. ' - ' .. item.statusText, domoticz.LOG_INFO)
                        end
    			    end
			    end
		    end
	    end
	end
}
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest