Dutch dynamic energy prices bar chart in Custom menu

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

Moderator: leecollings

Post Reply
HvdW
Posts: 532
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Dutch dynamic energy prices bar chart in Custom menu

Post by HvdW »

The Enever plugin made me think about making a bar chart to show dynamic electricity prices for today and tomorrow.

Preparation
- In Setup->Users->your name or admin activate the Custom menu and save
- get yourself a token from enever to be able to access the API
- prepare your RPI (example lightttpd) because you need to be able to update files on /var/www/html
- go /var/www/html

Code: Select all

touch Energy_Prices_Today.csv 
touch Energy_Prices_Tomorrow.csv
sudo chmod 664 /var/www/html/Energy_Prices_Today.csv
sudo chmod 664 /var/www/html/Energy_Prices_Tomorrow.csv
sudo chown www-data:www-data /var/www/html/Energy_Prices_Today.csv
sudo chown www-data:www-data /var/www/html/Energy_Prices_Tomorrow.csv
 
- copy these 2 dzVents scripts

Code: Select all

return {
    on = {
        timer = { 'at 00:05' }, -- a new day yesterday, is an old day now 
        httpResponses = { 'EnergyPricesTodayReceived' }
    },
    logging = {
        level = domoticz.LOG_INFO,
        marker = 'EnergyPricesToday'
    },
    execute = function(domoticz, item)
        local energyProviders = {
            --prijs = "Beurs",
            AA = "Atoom Alliantie",
            AIP = "All in power",
            ANWB = "ANWB Energie",
            BE = "Budget Energie",
            EE = "EasyEnergy",
            EN = "Eneco",
            EVO = "Energie van Ons",
            EZ = "Energy Zero",
            FR = "Frank Energie",
            GSL = "Groenestroom Lokaal",
            MDE = "Mijndomein Energie",
            NE = "NextEnergy",
            TI = "Tibber",
            VDB = "Vandebron",
            VON = "Vrij op naam",
            WE = "Wout Energie",
            ZG = "ZonderGas",
            ZP = "Zonneplan"
        }

        if (item.isTimer) then
            domoticz.log('Starting energy prices update', domoticz.LOG_INFO)
            -- Doe het API verzoek
            domoticz.openURL({
                url = 'https://enever.nl/api/stroomprijs_vandaag.php?token='<your_enever_token>',
                method = 'GET',
                callback = 'EnergyPricesTodayReceived'
            })
        elseif (item.isHTTPResponse) then
            domoticz.log('Processing HTTP response', domoticz.LOG_INFO)
            if (item.ok) then
                local data = item.json
                if data.status == "true" then
                    -- Open het CSV-bestand om te schrijven
                    local file = io.open("/var/www/html/Energy_Prices_Today.csv", "w")
                    if file then
                        -- Schrijf de header naar het bestand
                        file:write("Afkorting,Provider,00:00,01:00,02:00,03:00,04:00,05:00,06:00,07:00,08:00,09:00,10:00,11:00,12:00,13:00,14:00,15:00,16:00,17:00,18:00,19:00,20:00,21:00,22:00,23:00\n")
                        
                        -- Schrijf de data voor elke provider
                        for provider, name in pairs(energyProviders) do
                            file:write(provider .. "," .. name)
                            for _, hour in ipairs(data.data) do
                                file:write(string.format(",%s", hour["prijs" .. provider]))
                            end
                            file:write("\n")
                        end
                        file:close()
                        domoticz.log('Energy prices saved to Energy_Prices_Today.csv', domoticz.LOG_INFO)
                    else
                        domoticz.log('ERROR: Could not open Energy_Prices_Today.csv for writing', domoticz.LOG_ERROR)
                    end
                else
                    domoticz.log('ERROR: Invalid response from API', domoticz.LOG_ERROR)
                end
            else
                domoticz.log('ERROR: HTTP request failed', domoticz.LOG_ERROR)
            end
        end
    end
}

Code: Select all

return {
    on = {
        timer = { 'every hour between 14:00 and 17:00', 'at 00:04' }, -- new prices arrive in the afternoon of the preceding day
        -- after midnight the tomorrow day needs to change
        httpResponses = { 'EnergyPricesTomorrowReceived' }
    },
    logging = {
        level = domoticz.LOG_INFO,
        marker = 'EnergyPricesTomorrow'
    },
    execute = function(domoticz, item)
        local energyProviders = {
            --prijs = "Beurs",
            AA = "Atoom Alliantie",
            AIP = "All in power",
            ANWB = "ANWB Energie",
            BE = "Budget Energie",
            EE = "EasyEnergy",
            EN = "Eneco",
            EVO = "Energie van Ons",
            EZ = "Energy Zero",
            FR = "Frank Energie",
            GSL = "Groenestroom Lokaal",
            MDE = "Mijndomein Energie",
            NE = "NextEnergy",
            TI = "Tibber",
            VDB = "Vandebron",
            VON = "Vrij op naam",
            WE = "Wout Energie",
            ZG = "ZonderGas",
            ZP = "Zonneplan"
        }
        -- Huidige tijd in seconden sinds epoch (1 januari 1970)
        local currentTime = os.time()

        -- Tijd in seconden voor één dag (24 uur * 60 minuten * 60 seconden)
        local oneDay = 24 * 60 * 60

        -- Tijd in seconden voor morgen
        local tomorrowTime = currentTime + oneDay

        -- Datum van morgen in het gewenste formaat
        local tomorrowDate = os.date("%d-%m-%Y %H:%M", tomorrowTime)

print("Datum van morgen: " .. tomorrowDate)


        if (item.isTimer) then
            domoticz.log('Starting energy prices tomorrow update', domoticz.LOG_INFO)
            -- Doe het API verzoek
            domoticz.openURL({
                url = 'https://enever.nl/api/stroomprijs_morgen.php?token=<your_enever_token',
                method = 'GET',
                callback = 'EnergyPricesTomorrowReceived'
            })
        elseif (item.isHTTPResponse) then
            domoticz.log('Processing HTTP response', domoticz.LOG_INFO)
            if (item.ok) then
                local data = item.json
                if data.status == "true" then
                    -- Open het CSV-bestand om te schrijven
                    local file = io.open("/var/www/html/Energy_Prices_Tomorrow.csv", "w")
                    if file then
                        -- Schrijf de header naar het bestand
                        file:write("Afkorting,Provider,00:00,01:00,02:00,03:00,04:00,05:00,06:00,07:00,08:00,09:00,10:00,11:00,12:00,13:00,14:00,15:00,16:00,17:00,18:00,19:00,20:00,21:00,22:00,23:00\n")
                        
                        -- Schrijf de data voor elke provider
                        for provider, name in pairs(energyProviders) do
                            file:write(provider .. "," .. name)
                            for _, hour in ipairs(data.data) do
                                file:write(string.format(",%s", hour["prijs" .. provider]))
                            end
                            file:write("\n")
                        end
                        file:close()
                        domoticz.log('Energy prices saved to Energy_Prices_Tomorrow.csv', domoticz.LOG_INFO)
                    else
                        domoticz.log('ERROR: Could not open Energy_Prices_Tomorrow.csv for writing', domoticz.LOG_INFO)
                    end
                else
                    domoticz.log('ERROR: Invalid response from API', domoticz.LOG_ERROR)
                end
            else
                domoticz.log('ERROR: HTTP request failed', domoticz.LOG_ERROR)
            end
        end
    end
}
- go /var/www/html and write the html that collects data and displays the data in a graph. Name of the file energyprices.html (omit the underscore from the other html to not mix up both files)

Code: Select all

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Energy Prices Staafdiagram</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        .container {
            width: 80%;
            margin: auto;
            text-align: center;
        }
        .dropdown {
            margin-bottom: 20px;
        }
    </style>
    <!-- Verwijzing naar de Chart.js bibliotheek via een CDN -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <div class="container">
        <h1>Energy Prices Staafdiagram</h1>
        <div class="dropdown">
            <label for="providerSelect">Kies een provider:</label>
            <select id="providerSelect">
                <option value="EN">Eneco</option>
                <option value="FR">Frank Energie</option>
                <option value="EVO">Energie van Ons</option>
                <option value="TI">Tibber</option>
                <option value="GSL">Groenestroom Lokaal</option>
                <option value="ANWB">ANWB Energie</option>
                <option value="ZG">ZonderGas</option>
                <option value="ZP">Zonneplan</option>
                <option value="EZ">Energy Zero</option>
                <option value="VON">Vrij op naam</option>
                <option value="EE">EasyEnergy</option>
                <option value="MDE">Mijndomein Energie</option>
                <option value="BE">Budget Energie</option>
                <option value="AA">Atoom Alliantie</option>
                <option value="WE">Wout Energie</option>
                <option value="VDB">Vandebron</option>
                <option value="NE">NextEnergy</option>
                <option value="AIP">All in power</option>
            </select>
        </div>
        <canvas id="energyPricesChart" width="800" height="400"></canvas>
        <p id="dates"></p>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const providerSelect = document.getElementById('providerSelect');
            const chartCanvas = document.getElementById('energyPricesChart');
            const datesElement = document.getElementById('dates');
            let chart;

            function fetchData(provider) {
                fetch('/Energy_Prices_Today.csv')
                    .then(response => response.text())
                    .then(todayData => {
                        console.log('Today Data:', todayData); // Controleer de opgehaalde data
                        fetch('/Energy_Prices_Tomorrow.csv')
                            .then(response => response.text())
                            .then(tomorrowData => {
                                console.log('Tomorrow Data:', tomorrowData); // Controleer de opgehaalde data
                                const todayPrices = parseCSV(todayData, provider);
                                const tomorrowPrices = parseCSV(tomorrowData, provider);
                                console.log('Today Prices:', todayPrices); // Controleer de verwerkte data
                                console.log('Tomorrow Prices:', tomorrowPrices); // Controleer de verwerkte data
                                const labels = generateLabels();
                                const combinedPrices = todayPrices.concat(tomorrowPrices);

                                const todayDate = new Date().toLocaleDateString();
                                const tomorrowDate = new Date(Date.now() + 86400000).toLocaleDateString();

                                datesElement.textContent = `Vandaag: ${todayDate}, Morgen: ${tomorrowDate}`;

                                if (chart) {
                                    chart.destroy();
                                }

                                chart = new Chart(chartCanvas, {
                                    type: 'bar',
                                    data: {
                                        labels: labels,
                                        datasets: [{
                                            label: provider,
                                            data: combinedPrices,
                                            backgroundColor: 'rgba(75, 192, 192, 0.2)',
                                            borderColor: 'rgba(75, 192, 192, 1)',
                                            borderWidth: 1
                                        }]
                                    },
                                    options: {
                                        scales: {
                                            x: {
                                                title: {
                                                    display: true,
                                                    text: 'Tijd'
                                                },
                                                ticks: {
                                                    color: 'black' // Kleur van de labels op de x-as
                                                }
                                            },
                                            y: {
                                                beginAtZero: true,
                                                title: {
                                                    display: true,
                                                    text: 'Prijs'
                                                },
                                                ticks: {
                                                    color: 'black' // Kleur van de labels op de y-as
                                                }
                                            }
                                        }
                                    }
                                });
                            });
                    });
            }

            function parseCSV(data, provider) {
                const lines = data.split('\n');
                const result = [];
                const providerIndex = lines[0].split(',').indexOf(provider);

                for (let i = 1; i < lines.length; i++) {
                    const columns = lines[i].split(',');
                    if (columns[0] === provider) {
                        for (let j = 2; j < columns.length; j++) {
                            result.push(parseFloat(columns[j]));
                        }
                        break;
                    }
                }

                return result;
            }

            function generateLabels() {
                const labels = [];
                for (let i = 0; i < 24; i++) {
                    labels.push(i.toString().padStart(2, '0') + ':00');
                }
                return labels.concat(labels);
            }

            providerSelect.addEventListener('change', function() {
                fetchData(providerSelect.value);
            });

            fetchData(providerSelect.value);
        });
    </script>
</body>
</html>
- test in your browser http://your_LAN_IP/energyprices.html
- write a file in /home/pi/domoticz/www/templates named energy_prices.html

Code: Select all

<IFRAME SRC="http://192.168.2.104/energyprices.html" height="800" width="100%"></IFRAME>
restart domoticz

Code: Select all

sudo service domoticz restart
- back in domoticz in your browser refresh with Ctrl-F5 to clear the buffer

PS In my situation it works as expected in Firefox and in Edge it doesn't. (help and advice appreciated)
Last edited by HvdW on Tuesday 31 December 2024 20:30, edited 1 time in total.
Bugs bug me.
User avatar
gizmocuz
Posts: 2394
Joined: Thursday 11 July 2013 18:59
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Top of the world
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by gizmocuz »

Maybe you could use the build in Enever hardware?
Quality outlives Quantity!
User avatar
waltervl
Posts: 5388
Joined: Monday 28 January 2019 18:48
Target OS: Linux
Domoticz version: 2024.7
Location: NL
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by waltervl »

What is the difference with the price graphs from the existing Enever plugin?
Screenshot_20241231-144158.png
Screenshot_20241231-144158.png (69.54 KiB) Viewed 296 times
Domoticz running on Udoo X86 (on Ubuntu)
Devices/plugins: ZigbeeforDomoticz (with Xiaomi, Ikea, Tuya devices), Nefit Easy, Midea Airco, Omnik Solar, Goodwe Solar
HvdW
Posts: 532
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by HvdW »

Relevant remark.
The short answer is I don't understand how to do so.
The idea of these scripts came because the Enever plugin doesn't show a table graph plus that it displays Daily Price and Actual Price which both are the same. The prices are historic prices and for f.i. car charging it is helpfull to know ahead prices. For smart charging calculations a table is sufficiënt but it's always nice to have data displayd in a bar graph.

What I had up till now was a Text sensor based upon the Enever plugin information that displays the price difference between fixed tariff and dynamic tariff.
Prijsverschil.jpg
Prijsverschil.jpg (13.99 KiB) Viewed 272 times
Meanwhile there's another script that displays dynamic prices as well as fixed prices.

Code: Select all

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Energy Prices Staafdiagram</title>
    <style>
        body {
            font-family: Arial, sans-serif;
        }
        .container {
            width: 80%;
            margin: auto;
            text-align: center.
        }
        .dropdown {
            margin-bottom: 20px;
        }
    </style>
    <!-- Verwijzing naar de Chart.js bibliotheek via een CDN -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
    <div class="container">
        <h1>Energy Prices Staafdiagram</h1>
        <div class="dropdown">
            <label for="providerSelect">Kies een provider:</label>
            <select id="providerSelect">
                <option value="EN">Eneco</option>
                <option value="FR">Frank Energie</option>
                <option value="EVO">Energie van Ons</option>
                <option value="TI">Tibber</option>
                <option value="GSL">Groenestroom Lokaal</option>
                <option value="ANWB">ANWB Energie</option>
                <option value="ZG">ZonderGas</option>
                <option value="ZP">Zonneplan</option>
                <option value="EZ">Energy Zero</option>
                <option value="VON">Vrij op naam</option>
                <option value="EE">EasyEnergy</option>
                <option value="MDE">Mijndomein Energie</option>
                <option value="BE">Budget Energie</option>
                <option value="AA">Atoom Alliantie</option>
                <option value="WE">Wout Energie</option>
                <option value="VDB">Vandebron</option>
                <option value="NE">NextEnergy</option>
                <option value="AIP">All in power</option>
            </select>
        </div>
        <canvas id="energyPricesChart" width="800" height="400"></canvas>
        <p id="dates"></p>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const providerSelect = document.getElementById('providerSelect');
            const chartCanvas = document.getElementById('energyPricesChart');
            const datesElement = document.getElementById('dates');
            let chart;

            function fetchData(provider) {
                fetch('/Energy_Prices_Today.csv')
                    .then(response => response.text())
                    .then(todayData => {
                        console.log('Today Data:', todayData); // Controleer de opgehaalde data
                        fetch('/Energy_Prices_Tomorrow.csv')
                            .then(response => response.text())
                            .then(tomorrowData => {
                                console.log('Tomorrow Data:', tomorrowData); // Controleer de opgehaalde data
                                const todayPrices = parseCSV(todayData, provider);
                                const tomorrowPrices = parseCSV(tomorrowData, provider);
                                console.log('Today Prices:', todayPrices); // Controleer de verwerkte data
                                console.log('Tomorrow Prices:', tomorrowPrices); // Controleer de verwerkte data
                                const labels = generateLabels();
                                const combinedPrices = todayPrices.concat(tomorrowPrices);

                                const todayDate = new Date().toLocaleDateString();
                                const tomorrowDate = new Date(Date.now() + 86400000).toLocaleDateString();

                                datesElement.textContent = `Vandaag: ${todayDate}, Morgen: ${tomorrowDate}`;

                                const offPeakPrices = Array(8).fill(0.234).concat(Array(16).fill(0.275)).concat(Array(8).fill(0.234)).concat(Array(8).fill(0.234)).concat(Array(16).fill(0.275)).concat(Array(8).fill(0.234));

                                if (chart) {
                                    chart.destroy();
                                }

                                chart = new Chart(chartCanvas, {
                                    type: 'bar',
                                    data: {
                                        labels: labels,
                                        datasets: [
                                            {
                                                label: provider,
                                                data: combinedPrices,
                                                backgroundColor: 'rgba(75, 192, 192, 0.2)',
                                                borderColor: 'rgba(75, 192, 192, 1)',
                                                borderWidth: 1
                                            },
                                            {
                                                label: 'Daltarief',
                                                data: offPeakPrices,
                                                backgroundColor: 'rgba(192, 75, 75, 0.2)',
                                                borderColor: 'rgba(192, 75, 75, 1)',
                                                borderWidth: 1
                                            }
                                        ]
                                    },
                                    options: {
                                        scales: {
                                            x: {
                                                title: {
                                                    display: true,
                                                    text: 'Tijd',
                                                    color: 'black' // Kleur van de titel op de x-as
                                                },
                                                ticks: {
                                                    color: 'black' // Kleur van de labels op de x-as
                                                }
                                            },
                                            y: {
                                                beginAtZero: true,
                                                title: {
                                                    display: true,
                                                    text: 'Prijs',
                                                    color: 'black' // Kleur van de titel op de y-as
                                                },
                                                ticks: {
                                                    color: 'black' // Kleur van de labels op de y-as
                                                }
                                            }
                                        }
                                    }
                                });
                            });
                    });
            }

            function parseCSV(data, provider) {
                const lines = data.split('\n');
                const result = [];
                const providerIndex = lines[0].split(',').indexOf(provider);

                for (let i = 1; i < lines.length; i++) {
                    const columns = lines[i].split(',');
                    if (columns[0] === provider) {
                        for (let j = 2; j < columns.length; j++) {
                            result.push(parseFloat(columns[j]));
                        }
                        break;
                    }
                }

                return result;
            }

            function generateLabels() {
                const labels = [];
                for (let i = 0; i < 24; i++) {
                    labels.push(i.toString().padStart(2, '0') + ':00');
                }
                return labels.concat(labels);
            }

            providerSelect.addEventListener('change', function() {
                fetchData(providerSelect.value);
            });

            fetchData(providerSelect.value);
        });
    </script>
</body>
</html>
One has to fill in its own prices in the above script.
Which brings me to an earlier question if it is possible or can be made possible to get the figures like Costs T1 and Costs T2 from settings -> meters/counters like domoticz.settings.costs_t1 or something alike.
Bugs bug me.
User avatar
waltervl
Posts: 5388
Joined: Monday 28 January 2019 18:48
Target OS: Linux
Domoticz version: 2024.7
Location: NL
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by waltervl »

No, the prices in the daily are the prices of the next or current day. See my screenshot at 14.45 showing the tariff of today at 20:00. So even now still in the future.

It is updated somewhere in the afternoon. It currently shows the tariffs per hour of tomorrow.
Screenshot_20241231-184051-087.png
Screenshot_20241231-184051-087.png (94.38 KiB) Viewed 265 times
Domoticz running on Udoo X86 (on Ubuntu)
Devices/plugins: ZigbeeforDomoticz (with Xiaomi, Ikea, Tuya devices), Nefit Easy, Midea Airco, Omnik Solar, Goodwe Solar
HvdW
Posts: 532
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by HvdW »

You're right.
I hadn't noticed.
Nevertheless it was nice doing the effort.
Bugs bug me.
User avatar
gizmocuz
Posts: 2394
Joined: Thursday 11 July 2013 18:59
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Top of the world
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by gizmocuz »

Yes, but not necessary, and you are polling the Enever server to much if you use both solutions.
But, could you post a screenshot of what you are, or have, made?
Or did you try to replicate the build-in Enever hardware? (Which also created additional utility devices for the current price of electricity and gas which you can use for price calculations)
Quality outlives Quantity!
HvdW
Posts: 532
Joined: Sunday 01 November 2015 22:45
Target OS: Raspberry Pi / ODroid
Domoticz version: 2023.2
Location: Twente
Contact:

Re: Dutch dynamic energy prices bar chart in Custom menu

Post by HvdW »

Screenshot_20250101_120135_Firefox.jpg
Screenshot_20250101_120135_Firefox.jpg (135.38 KiB) Viewed 216 times
The script does very little polling.
After midnight to adjust tomorrow which is today by now and several times in the afternoon grabbing the new tomorrow data which arrive between 2 and 5 in the afternoon of the current today.

The why is about the Enever plugin from which I am not able to extract future data from and my preoccupation about mastering Custom pages where I have found that it is not possible to write a script that needs to fetch external data so I'm using the concept as explained in the Custom page wiki.
The data are now stored in files. I could have used tables in memory.
Bugs bug me.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest