# Module `lib.plugins.electricmeter.htmlpage`

Web page of electric meter

Expand source code

    # Distributed under Pycameresp License
    # Copyright (c) 2023 Remi BERTHOLET
    ''' Web page of electric meter '''
    page = b"""
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
    <canvas id="line-chart"></canvas>
    <span id="legend"></span>
    <script>
    var STEP         = %d;
    var DAY_SELECTED = '%s';
    var DATE         = '%s';
    var CHART_TYPE   = '%s';
    var chart   = null;

    function pad(num, size)
    {
            num = parseInt(num);
            num = num.toString();
            while (num.length < size)
            {
                    num = "0" + num;
            }
            return num;
    }

    function capitalize(string)
    {
            return string.charAt(0).toUpperCase() + string.slice(1);
    }

    class Cost
    {
            constructor(name, currency, color)
            {
                    this.total_cost   = 0;
                    this.total_pulses = 0;
                    this.currency = currency;
                    this.name  = name;
                    this.color = color;
                    this.pulses = [];
                    this.costs  = [];
            }
    }

    class Costs
    {
            constructor(datas)
            {
                    this.costs = new Map();
                    this.datas = datas;
                    this.labels = [];
                    this.step_pulses = 0;
                    for (let i = 0; i < this.datas.rates.length; i++)
                    {
                            var rate = this.datas.rates[i];
                            if (!this.costs.has(rate.rate))
                            {
                                    this.costs.set(rate.rate, new Cost(rate.rate, rate.currency, rate.color));
                            }
                    }
            }

            add_step(minute, pulses)
            {
                    var rate = null;
                    minute *= 60;
                    for (let i = 0; i < this.datas.rates.length; i++)
                    {
                            rate = this.datas.rates[i];
                            if (minute >= rate.start_time && minute <= rate.end_time)
                            {
                                    rate = this.datas.rates[i];
                                    break;
                            }
                    }

                    for (var [name, cost] of this.costs)
                    {
                            if (cost.name === rate.rate)
                            {
                                    var current_cost = (rate.price / 1000.) * pulses
                                    if (STEP <= 15)
                                    {
                                            cost.pulses.push(pulses);
                                    }
                                    else
                                    {
                                            cost.pulses.push((pulses/1000.).toFixed(2));
                                    }
                                    cost.costs.push(current_cost.toFixed(2));
                                    cost.total_cost   += current_cost;
                                    cost.total_pulses += pulses;
                            }
                            else
                            {
                                    cost.pulses.push(0);
                                    cost.costs.push(0);
                            }
                    }
            }

            parse_step(minute, pulses)
            {
                    this.step_pulses += pulses;
                    if ((minute + 1)%%STEP == 0)
                    {
                            var time = pad((minute+1)/60, 2) + ":" + pad((minute+1)%%60, 2);
                            this.labels.push(time);
                            this.add_step(minute, this.step_pulses);
                            this.step_pulses = 0;
                    }
            }

            parse()
            {
                    for (let minute = 0; minute < this.datas.pulses.length; minute++)
                    {
                            this.parse_step(minute, this.datas.pulses[minute]);
                    }
            }

            get_data_set()
            {
                    var result = [];
                    var data = null;
                    var unit = "";
                    for (var [name, cost] of this.costs)
                    {
                            if (CHART_TYPE === "price")
                            {
                                    unit = cost.currency;
                                    data = cost.costs;
                            }
                            else
                            {
                                    if (STEP <= 15)
                                    {
                                            unit = "Wh";
                                    }
                                    else
                                    {
                                            unit = "kWh";
                                    }
                                    data = cost.pulses;
                            }
                            result.push(
                                    {
                                            data: data,
                                            label: cost.name + " (" + unit + ")",
                                            backgroundColor: cost.color,
                                            fill: false,
                                            pointRadius:1,
                                            borderWidth:0
                                    });

                    }

                    return result;
            }

            get_labels()
            {
                    return this.labels;
            }

            get_legend()
            {
                    var legend = "<br>";
                    var result = capitalize(DATE) + ' : ';
                    var total = 0;
                    var currency = "";
                    if (CHART_TYPE === "price")
                    {
                            for (var [name, cost] of this.costs)
                            {
                                    total += cost.total_cost;
                                    currency = cost.currency;
                                    legend += "<li>" + cost.name  + " : " + cost.total_cost.toFixed(2) + " " + cost.currency + "</li>";
                            }
                            result += total.toFixed(2) + currency;
                    }
                    else
                    {
                            for (var [name, cost] of this.costs)
                            {
                                    total += cost.total_pulses;
                                    legend += "<li>" + cost.name  + " : " + (cost.total_pulses / 1000).toFixed(2) + " kWh </li>";
                            }
                            result += (total/1000).toFixed(2) + " kWh";
                    }
                    document.getElementById("legend").innerHTML="<ul>" + capitalize(legend) + "</ul>";
                    return result;
            }
    }

    function show_chart(datas)
    {
            var costs = new Costs(datas);

            costs.parse();
            if (!(chart === null))
            {
                    chart.destroy();
                    chart = null;
            }
            costs.get_legend();
            chart = new Chart(document.getElementById("line-chart"),
            {
                    type: 'bar',
                    data:
                    {
                            labels: costs.get_labels(),
                            datasets: costs.get_data_set()
                    },
                    options:
                    {
                            responsive: true,
                            animation:
                            {
                                    duration: 0
                            },
                            plugins:
                            {
                                    legend:
                                    {
                                            position: 'top',
                                    },
                                    title:
                                    {
                                            display: true,
                                            text: costs.get_legend()
                                    }
                            },
                            scales:
                            {
                                    x:
                                    {
                                            stacked: true,
                                    }
                            }
                    }
            });
    }

    async function download_datas()
    {
            const response = await fetch('/pulses_rates?day='+DAY_SELECTED);
            var data = await response.json();
            show_chart(data);
    }

    download_datas();

    const d = new Date();
    var currentDate = d.getFullYear() + "-" + pad(d.getMonth()+1,2) + "-" + pad(d.getDate(),2);
    if (DAY_SELECTED === currentDate)
    {
            setInterval(download_datas, 5000);
    }
    </script>
    """