Module lib.plugins.electricmeter.webpage

Web page displaying electricity consumption per minute, day, month. The principle is based on counting the pulses generated by the electric meter, via the LED which flashes on the electric meter (1 wh per flash).

Expand source code
# Distributed under Pycameresp License
# Copyright (c) 2023 Remi BERTHOLET
""" Web page displaying electricity consumption per minute, day, month. 
The principle is based on counting the pulses generated by the electric meter, 
via the LED which flashes on the electric meter (1 wh per flash). """
# pylint:disable=anomalous-unicode-escape-in-string
# pylint:disable=wrong-import-order
# pylint:disable=unused-import
import json
import server.httpserver
from htmltemplate           import *
import webpage.mainpage
import tools.date
import tools.strings
import tools.lang
from plugins import electricmeter
from plugins.electricmeter import lang

@server.httpserver.HttpServer.add_route(b'/hourly', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_hour)
async def hourly_page_page(request, response, args):
        """ Daily consumption graph hour by hour """
        geolocation = electricmeter.config.GeolocationConfig.get_config()
        day  = tools.date.html_to_date(request.params.get(b"day",b""))

        if   request.params.get(b"direction",b"") == b"next":
                day += 86400
        elif request.params.get(b"direction",b"") == b"previous":
                day -= 86400

        step = int(request.params.get(b"step",b"1"))
        steps = []
        for s in [1,5,10,15,30]:
                if s == step:
                        selected = True
                else:
                        selected = False
                steps.append(Option(text=b"%d %s"%(s, electricmeter.lang.step_minutes), value=b"%d"%s, selected=selected))

        unit = request.params.get(b"unit",b"power")

        with  open(WWW_DIR + "electricmeter.html", "rb") as file:
                content = file.read()

        link_up = b"daily?year="+tools.date.date_to_html(day)[0:4] + b"&month="+tools.date.date_to_html(day)[5:7] + b"&unit=" + unit

        temperature = request.params.get(b"temperature")
        if temperature is None or temperature == b"0":
                with_temperature = b"false"
                temperature = None
        else:
                with_temperature = b"true"
                link_up += b"&temperature=1"

        page_content = \
        [
                Div(
                [
                        Div(
                        [
                                Button(type=b"submit", text=b"<-", name=b"direction",  value=b"previous"),Space(),
                                Link(type=b"button", class_=b"btn btn-outline-primary", text=b"\xE2\xA4\xB4", href=link_up),Space(),
                                Button(type=b"submit", text=b"->", name=b"direction",  value=b"next")
                        ], class_=b'col-md-2 mb-1'),
                        Div(
                        [
                                Input (type=b"date",   class_=b"form-label border rounded p-1", name=b"day", value= tools.date.date_to_html(day), event=b'onchange="this.form.submit()"'),Space(),
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Select(steps, spacer=b"", text=electricmeter.lang.step_minutes, name=b"step",                 event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2'),
                        Div([
                                Select(
                                [
                                        Option(text=electricmeter.lang.type_price, value=b"price", selected= True if unit == b"price" else False),
                                        Option(text=electricmeter.lang.type_power, value=b"power", selected= True if unit == b"power" else False),
                                ], spacer=b"", text=electricmeter.lang.step_minutes, name=b"unit",                 event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-1'),
                        Div(
                        [
                                Switch(text=electricmeter.lang.temperature, name=b"temperature", checked=temperature, event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2')
                ], class_=b"row"),

                Tag(content%(b"hourly",
                        step,
                        tools.date.date_to_html(day),
                        tools.lang.translate_date(day),
                        unit,
                        electricmeter.lang.power_consumed,
                        geolocation.latitude,
                        geolocation.longitude,
                        with_temperature))
        ]
        page = webpage.mainpage.main_frame(request, response, args,electricmeter.lang.title_electricmeter + electricmeter.lang.item_hour.lower(),Form(page_content))
        await response.send_page(page)

@server.httpserver.HttpServer.add_route(b'/hourly_datas')
async def hourly_page_datas(request, response, args):
        """ Send pulses of hours and rates """
        day    = tools.date.html_to_date(request.params.get(b"day",b""))
        result = {"pulses":None}
        result["rates"] = []
        for cost in electricmeter.config.TimeSlotsConfig.get_cost(day):
                result["rates"].append(cost.to_dict())
        try:
                result["pulses"] = electricmeter.meter.HourlyCounter.get_datas(day)
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(result)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)

@server.httpserver.HttpServer.add_route(b'/power_datas')
async def power_page_datas(request, response, args):
        """ Send current power consumed """
        try:
                power = {"power":electricmeter.meter.HourlyCounter.get_power()}
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(power)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)

@server.httpserver.HttpServer.add_route(b'/daily', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_day)
async def daily_page(request, response, args):
        """ Consumption graph day by day """
        geolocation = electricmeter.config.GeolocationConfig.get_config()
        y,m = tools.date.local_time()[:2]
        year   = int(request.params.get(b"year",b"%d"%y))
        month  = int(request.params.get(b"month",b"%d"%m))

        if   request.params.get(b"direction",b"") == b"next":
                if month == 12:
                        month = 1
                        year += 1
                else:
                        month += 1
        elif request.params.get(b"direction",b"") == b"previous":
                if month == 1:
                        month = 12
                        year -= 1
                else:
                        month -= 1

        day  = tools.date.html_to_date(b"%04d-%02d-01"%(year, month))

        unit = request.params.get(b"unit",b"power")
        temperature = request.params.get(b"temperature")
        if temperature is None or temperature == b"0":
                with_temperature = b"false"
                temperature = None
        else:
                with_temperature = b"true"

        months_combo = []
        month_id = 1
        for m in tools.lang.months:
                months_combo.append(Option(text=m, value=b"%d"%month_id, selected= True if month_id == month else False))
                month_id += 1

        with  open(WWW_DIR + "electricmeter.html", "rb") as file:
                content = file.read()

        page_content = \
        [
                Div(
                [
                        Div(
                        [
                                Button(type=b"submit", text=b"<-", name=b"direction",  value=b"previous"),Space(),
                                Link(type=b"button", class_=b"btn btn-outline-primary", text=b"\xE2\xA4\xB4", href=b"monthly?year="+tools.date.date_to_html(day)[0:4]),Space(),
                                Button(type=b"submit", text=b"->", name=b"direction",  value=b"next")
                        ], class_=b'col-md-2 mb-2'),
                        Div(
                        [
                                Edit (name=b"year", spacer=b"", type=b"number", step=b"1",  required=True, value=b"%d"%year, event=b'onchange="this.form.submit()"'),
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Select(months_combo, spacer=b"", name=b"month", event=b'onchange="this.form.submit()"'),
                        ], class_=b'col-md-2 mb-2'),
                        Div(
                        [
                                Select(
                                [
                                        Option(text=electricmeter.lang.type_price, value=b"price", selected= True if unit == b"price" else False),
                                        Option(text=electricmeter.lang.type_power, value=b"power", selected= True if unit == b"power" else False),
                                ], spacer=b"", text=electricmeter.lang.step_minutes, name=b"unit",                 event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Switch(text=electricmeter.lang.temperature, name=b"temperature", checked=temperature, event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2')
                ], class_=b"row"),
                Tag(content%(b"daily", 86400, tools.date.date_to_html(day), tools.lang.translate_date(day, False), unit, electricmeter.lang.power_consumed, geolocation.latitude, geolocation.longitude, with_temperature))
        ]
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_electricmeter + electricmeter.lang.item_day.lower(),Form(page_content))
        await response.send_page(page)

@server.httpserver.HttpServer.add_route(b'/daily_datas')
async def daily_datas(request, response, args):
        """ Send pulses of month and rates """
        month    = tools.date.html_to_date(request.params.get(b"month",b""))
        result = {"time_slots":None}
        result["rates"] = []
        for cost in electricmeter.config.TimeSlotsConfig.get_cost(month):
                result["rates"].append(cost.to_dict())
        try:
                result["time_slots"] = electricmeter.meter.DailyCounter.get_datas(month)
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(result)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)

@server.httpserver.HttpServer.add_route(b'/monthly', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_month)
async def monthly_page(request, response, args):
        """ Consumption graph month by month """
        geolocation = electricmeter.config.GeolocationConfig.get_config()
        y = tools.date.local_time()[0]
        year   = int(request.params.get(b"year",b"%d"%y))

        if   request.params.get(b"direction",b"") == b"next":
                year += 1
        elif request.params.get(b"direction",b"") == b"previous":
                year -= 1

        month  = tools.date.html_to_date(b"%04d-01-01"%(year))
        unit = request.params.get(b"unit",b"power")

        with  open(WWW_DIR + "electricmeter.html", "rb") as file:
                content = file.read()

        page_content = \
        [
                Div(
                [
                        Div(
                        [
                                Button(type=b"submit", text=b"<-", name=b"direction",  value=b"previous"),Space(),
                                Button(type=b"submit", text=b"->", name=b"direction",  value=b"next")
                        ], class_=b'col-md-2 mb-2'),
                        Div(\
                        [
                                Edit (name=b"year", spacer=b"", type=b"number", step=b"1",  required=True, value=b"%d"%year, event=b'onchange="this.form.submit()"'),
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Select(
                                [
                                        Option(text=electricmeter.lang.type_price, value=b"price", selected= True if unit == b"price" else False),
                                        Option(text=electricmeter.lang.type_power, value=b"power", selected= True if unit == b"power" else False),
                                ], spacer=b"", text=electricmeter.lang.step_minutes, name=b"unit",                 event=b'onchange="this.form.submit()"')
                        ],class_=b'col-md-2')
                ], class_=b"row"),

                Tag(content%(b"monthly", 86400, tools.date.date_to_html(month), tools.lang.translate_date(month, False), unit, electricmeter.lang.power_consumed, geolocation.latitude, geolocation.longitude, b""))
        ]
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_electricmeter + electricmeter.lang.item_month.lower(),Form(page_content))
        await response.send_page(page)

@server.httpserver.HttpServer.add_route(b'/monthly_datas')
async def monthly_datas(request, response, args):
        """ Send pulses of month and rates """
        year   = tools.date.html_to_date(request.params.get(b"year",b""))
        result = {"time_slots":None}
        result["rates"] = []
        for cost in electricmeter.config.TimeSlotsConfig.get_cost(year):
                result["rates"].append(cost.to_dict())
        try:
                result["time_slots"] = electricmeter.meter.MonthlyCounter.get_datas(year)
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(result)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)

@server.httpserver.HttpServer.add_route(b'/rate', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_rate)
async def rate_page(request, response, args):
        """ Electric rate configuration page """
        rates   = electricmeter.config.RatesConfig.get_config()
        current = electricmeter.config.RateConfig()

        # If new rate added
        if request.params.get(b"add",None) is not None:
                current.update(request.params, show_error=False)
                rates.append(current)
                rates.save()
        # If a rate must be deleted
        elif request.params.get(b"remove",None) is not None:
                rates.remove(request.params.get(b"remove",b"none"))
                rates.save()
        # If a rate must be edited
        elif request.params.get(b"edit",None) is not None:
                current = rates.get(request.params.get(b"edit",b"none"))

        # List all rates
        rate_items = []
        identifier = 0
        while True:
                rate = rates.get(identifier)
                if rate is not None:
                        rate_items.append(
                                        ListItem(
                                        [
                                                Link(text=b" %s : %f %s %s %s "%(rate.name, rate.price, rate.currency, electricmeter.lang.from_the, tools.lang.translate_date(rate.validity_date)), href=b"rate?edit=%d"%identifier ),
                                                Link(text=electricmeter.lang.remove_button , class_=b"btn position-absolute top-50 end-0 translate-middle-y", href=b"rate?remove=%d"%identifier, onclick=b"return window.confirm('%s')"%electricmeter.lang.remove_dialog)
                                        ]))
                else:
                        break
                identifier += 1

        # Build page
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_rate,
        [
                Form(
                [
                        Edit  (text=electricmeter.lang.name,          name=b"name",          placeholder=electricmeter.lang.field_rate,     required=True, value=current.name),
                        Edit  (text=electricmeter.lang.validy_date,   name=b"validity_date", type=b"date",                    required=True, value=b"%04d-%02d-%02d"%tools.date.local_time(current.validity_date)[:3]),
                        Edit  (text=electricmeter.lang.price,         name=b"price",         type=b"number", step=b"0.0001",  required=True, value=b"%f"%current.price),
                        Edit  (text=electricmeter.lang.currency,      name=b"currency",      placeholder=electricmeter.lang.field_currency, required=True, value=current.currency),
                        Submit(text=electricmeter.lang.add_button,    name=b"add")
                ]),
                List(rate_items)
        ])
        await response.send_page(page)

@server.httpserver.HttpServer.add_route(b'/time_slots', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_time_slots)
async def time_slots_page(request, response, args):
        """ Electric time slots configuration page """
        time_slots = electricmeter.config.TimeSlotsConfig.get_config()
        rates   = electricmeter.config.RatesConfig.get_config()
        current = electricmeter.config.TimeSlotConfig()

        # If new time_slot added
        if request.params.get(b"add",None) is not None:
                current.update(request.params, show_error=False)
                time_slots.append(current)
                time_slots.save()
        # If a time_slot must be deleted
        elif request.params.get(b"remove",None) is not None:
                time_slots.remove(request.params.get(b"remove",b"none"))
                time_slots.save()
        # If a time_slot must be edited
        elif request.params.get(b"edit",None) is not None:
                current = time_slots.get(request.params.get(b"edit",b"none"))

        # List all rates names
        rates_combo = []
        names       = {}
        identifier  = 0
        while True:
                rate = rates.get(identifier)
                if rate is not None:
                        if rate.name not in names:
                                rates_combo.append(Option(text=rate.name, value=rate.name, selected=b"selected" if current.rate == rate.name else b""))
                else:
                        break
                names[rate.name] = b""
                identifier += 1

        # List all rates
        time_slots_items = []
        identifier = 0
        while True:
                time_slot = time_slots.get(identifier)
                if time_slot is not None:
                        time_slots_items.append(
                                        ListItem(
                                        [
                                                Label(text=b"    ", style=b"background-color: %s"%time_slot.color),
                                                Space(),Space(),
                                                Link(text=b"%s - %s : %s "%(tools.date.time_to_html(time_slot.start_time), tools.date.time_to_html(time_slot.end_time), time_slot.rate),href=b"time_slots?edit=%d"%identifier),
                                                Link(text=electricmeter.lang.remove_button , class_=b"btn position-absolute top-50 end-0 translate-middle-y", href=b"time_slots?remove=%d"%identifier, onclick=b"return window.confirm('%s')"%electricmeter.lang.remove_dialog)
                                        ]))
                else:
                        break
                identifier += 1

        # Build page
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_time_slots,
        [
                Form(
                [
                        Edit (text=electricmeter.lang.field_start,      name=b"start_time", type=b"time", required=True, value=tools.date.time_to_html(current.start_time)),
                        Edit (text=electricmeter.lang.field_end,        name=b"end_time",   type=b"time", required=True, value=tools.date.time_to_html(current.end_time)),
                        Label(text=electricmeter.lang.field_time_rate),
                        Select(rates_combo,name=b"rate",                                    required=True),
                        Edit (text=electricmeter.lang.field_color,      name=b"color", type=b"color",     required=True, value=current.color),
                        Submit(text=electricmeter.lang.add_button,      name=b"add")
                ]),
                List(time_slots_items)
        ])
        await response.send_page(page)

@server.httpserver.HttpServer.add_route(b'/geolocation', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_geolocation)
async def geolocation_page(request, response, args):
        """ Determines the geolocation of the device """
        config = electricmeter.config.GeolocationConfig.get_config()
        disabled, action, submit = webpage.mainpage.manage_default_button(request, config)

        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.item_geolocation,
                Form(
                [
                        Edit  (text=electricmeter.lang.latitude,  name=b"latitude",  type=b"number", step=b"0.0001", required=True, min=b"-90.",  max=b"90." , value=b"%.3f"%config.latitude, disabled=disabled),
                        Edit  (text=electricmeter.lang.longitude, name=b"longitude", type=b"number", step=b"0.0001", required=True, min=b"-180.", max=b"180.", value=b"%.3f"%config.longitude,disabled=disabled),
                        submit, None
                ]))
        await response.send_page(page)

Functions

async def daily_datas(request, response, args)

Send pulses of month and rates

Expand source code
@server.httpserver.HttpServer.add_route(b'/daily_datas')
async def daily_datas(request, response, args):
        """ Send pulses of month and rates """
        month    = tools.date.html_to_date(request.params.get(b"month",b""))
        result = {"time_slots":None}
        result["rates"] = []
        for cost in electricmeter.config.TimeSlotsConfig.get_cost(month):
                result["rates"].append(cost.to_dict())
        try:
                result["time_slots"] = electricmeter.meter.DailyCounter.get_datas(month)
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(result)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)
async def daily_page(request, response, args)

Consumption graph day by day

Expand source code
@server.httpserver.HttpServer.add_route(b'/daily', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_day)
async def daily_page(request, response, args):
        """ Consumption graph day by day """
        geolocation = electricmeter.config.GeolocationConfig.get_config()
        y,m = tools.date.local_time()[:2]
        year   = int(request.params.get(b"year",b"%d"%y))
        month  = int(request.params.get(b"month",b"%d"%m))

        if   request.params.get(b"direction",b"") == b"next":
                if month == 12:
                        month = 1
                        year += 1
                else:
                        month += 1
        elif request.params.get(b"direction",b"") == b"previous":
                if month == 1:
                        month = 12
                        year -= 1
                else:
                        month -= 1

        day  = tools.date.html_to_date(b"%04d-%02d-01"%(year, month))

        unit = request.params.get(b"unit",b"power")
        temperature = request.params.get(b"temperature")
        if temperature is None or temperature == b"0":
                with_temperature = b"false"
                temperature = None
        else:
                with_temperature = b"true"

        months_combo = []
        month_id = 1
        for m in tools.lang.months:
                months_combo.append(Option(text=m, value=b"%d"%month_id, selected= True if month_id == month else False))
                month_id += 1

        with  open(WWW_DIR + "electricmeter.html", "rb") as file:
                content = file.read()

        page_content = \
        [
                Div(
                [
                        Div(
                        [
                                Button(type=b"submit", text=b"<-", name=b"direction",  value=b"previous"),Space(),
                                Link(type=b"button", class_=b"btn btn-outline-primary", text=b"\xE2\xA4\xB4", href=b"monthly?year="+tools.date.date_to_html(day)[0:4]),Space(),
                                Button(type=b"submit", text=b"->", name=b"direction",  value=b"next")
                        ], class_=b'col-md-2 mb-2'),
                        Div(
                        [
                                Edit (name=b"year", spacer=b"", type=b"number", step=b"1",  required=True, value=b"%d"%year, event=b'onchange="this.form.submit()"'),
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Select(months_combo, spacer=b"", name=b"month", event=b'onchange="this.form.submit()"'),
                        ], class_=b'col-md-2 mb-2'),
                        Div(
                        [
                                Select(
                                [
                                        Option(text=electricmeter.lang.type_price, value=b"price", selected= True if unit == b"price" else False),
                                        Option(text=electricmeter.lang.type_power, value=b"power", selected= True if unit == b"power" else False),
                                ], spacer=b"", text=electricmeter.lang.step_minutes, name=b"unit",                 event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Switch(text=electricmeter.lang.temperature, name=b"temperature", checked=temperature, event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2')
                ], class_=b"row"),
                Tag(content%(b"daily", 86400, tools.date.date_to_html(day), tools.lang.translate_date(day, False), unit, electricmeter.lang.power_consumed, geolocation.latitude, geolocation.longitude, with_temperature))
        ]
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_electricmeter + electricmeter.lang.item_day.lower(),Form(page_content))
        await response.send_page(page)
async def geolocation_page(request, response, args)

Determines the geolocation of the device

Expand source code
@server.httpserver.HttpServer.add_route(b'/geolocation', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_geolocation)
async def geolocation_page(request, response, args):
        """ Determines the geolocation of the device """
        config = electricmeter.config.GeolocationConfig.get_config()
        disabled, action, submit = webpage.mainpage.manage_default_button(request, config)

        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.item_geolocation,
                Form(
                [
                        Edit  (text=electricmeter.lang.latitude,  name=b"latitude",  type=b"number", step=b"0.0001", required=True, min=b"-90.",  max=b"90." , value=b"%.3f"%config.latitude, disabled=disabled),
                        Edit  (text=electricmeter.lang.longitude, name=b"longitude", type=b"number", step=b"0.0001", required=True, min=b"-180.", max=b"180.", value=b"%.3f"%config.longitude,disabled=disabled),
                        submit, None
                ]))
        await response.send_page(page)
async def hourly_page_datas(request, response, args)

Send pulses of hours and rates

Expand source code
@server.httpserver.HttpServer.add_route(b'/hourly_datas')
async def hourly_page_datas(request, response, args):
        """ Send pulses of hours and rates """
        day    = tools.date.html_to_date(request.params.get(b"day",b""))
        result = {"pulses":None}
        result["rates"] = []
        for cost in electricmeter.config.TimeSlotsConfig.get_cost(day):
                result["rates"].append(cost.to_dict())
        try:
                result["pulses"] = electricmeter.meter.HourlyCounter.get_datas(day)
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(result)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)
async def hourly_page_page(request, response, args)

Daily consumption graph hour by hour

Expand source code
@server.httpserver.HttpServer.add_route(b'/hourly', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_hour)
async def hourly_page_page(request, response, args):
        """ Daily consumption graph hour by hour """
        geolocation = electricmeter.config.GeolocationConfig.get_config()
        day  = tools.date.html_to_date(request.params.get(b"day",b""))

        if   request.params.get(b"direction",b"") == b"next":
                day += 86400
        elif request.params.get(b"direction",b"") == b"previous":
                day -= 86400

        step = int(request.params.get(b"step",b"1"))
        steps = []
        for s in [1,5,10,15,30]:
                if s == step:
                        selected = True
                else:
                        selected = False
                steps.append(Option(text=b"%d %s"%(s, electricmeter.lang.step_minutes), value=b"%d"%s, selected=selected))

        unit = request.params.get(b"unit",b"power")

        with  open(WWW_DIR + "electricmeter.html", "rb") as file:
                content = file.read()

        link_up = b"daily?year="+tools.date.date_to_html(day)[0:4] + b"&month="+tools.date.date_to_html(day)[5:7] + b"&unit=" + unit

        temperature = request.params.get(b"temperature")
        if temperature is None or temperature == b"0":
                with_temperature = b"false"
                temperature = None
        else:
                with_temperature = b"true"
                link_up += b"&temperature=1"

        page_content = \
        [
                Div(
                [
                        Div(
                        [
                                Button(type=b"submit", text=b"<-", name=b"direction",  value=b"previous"),Space(),
                                Link(type=b"button", class_=b"btn btn-outline-primary", text=b"\xE2\xA4\xB4", href=link_up),Space(),
                                Button(type=b"submit", text=b"->", name=b"direction",  value=b"next")
                        ], class_=b'col-md-2 mb-1'),
                        Div(
                        [
                                Input (type=b"date",   class_=b"form-label border rounded p-1", name=b"day", value= tools.date.date_to_html(day), event=b'onchange="this.form.submit()"'),Space(),
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Select(steps, spacer=b"", text=electricmeter.lang.step_minutes, name=b"step",                 event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2'),
                        Div([
                                Select(
                                [
                                        Option(text=electricmeter.lang.type_price, value=b"price", selected= True if unit == b"price" else False),
                                        Option(text=electricmeter.lang.type_power, value=b"power", selected= True if unit == b"power" else False),
                                ], spacer=b"", text=electricmeter.lang.step_minutes, name=b"unit",                 event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-1'),
                        Div(
                        [
                                Switch(text=electricmeter.lang.temperature, name=b"temperature", checked=temperature, event=b'onchange="this.form.submit()"')
                        ], class_=b'col-md-2')
                ], class_=b"row"),

                Tag(content%(b"hourly",
                        step,
                        tools.date.date_to_html(day),
                        tools.lang.translate_date(day),
                        unit,
                        electricmeter.lang.power_consumed,
                        geolocation.latitude,
                        geolocation.longitude,
                        with_temperature))
        ]
        page = webpage.mainpage.main_frame(request, response, args,electricmeter.lang.title_electricmeter + electricmeter.lang.item_hour.lower(),Form(page_content))
        await response.send_page(page)
async def monthly_datas(request, response, args)

Send pulses of month and rates

Expand source code
@server.httpserver.HttpServer.add_route(b'/monthly_datas')
async def monthly_datas(request, response, args):
        """ Send pulses of month and rates """
        year   = tools.date.html_to_date(request.params.get(b"year",b""))
        result = {"time_slots":None}
        result["rates"] = []
        for cost in electricmeter.config.TimeSlotsConfig.get_cost(year):
                result["rates"].append(cost.to_dict())
        try:
                result["time_slots"] = electricmeter.meter.MonthlyCounter.get_datas(year)
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(result)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)
async def monthly_page(request, response, args)

Consumption graph month by month

Expand source code
@server.httpserver.HttpServer.add_route(b'/monthly', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_month)
async def monthly_page(request, response, args):
        """ Consumption graph month by month """
        geolocation = electricmeter.config.GeolocationConfig.get_config()
        y = tools.date.local_time()[0]
        year   = int(request.params.get(b"year",b"%d"%y))

        if   request.params.get(b"direction",b"") == b"next":
                year += 1
        elif request.params.get(b"direction",b"") == b"previous":
                year -= 1

        month  = tools.date.html_to_date(b"%04d-01-01"%(year))
        unit = request.params.get(b"unit",b"power")

        with  open(WWW_DIR + "electricmeter.html", "rb") as file:
                content = file.read()

        page_content = \
        [
                Div(
                [
                        Div(
                        [
                                Button(type=b"submit", text=b"<-", name=b"direction",  value=b"previous"),Space(),
                                Button(type=b"submit", text=b"->", name=b"direction",  value=b"next")
                        ], class_=b'col-md-2 mb-2'),
                        Div(\
                        [
                                Edit (name=b"year", spacer=b"", type=b"number", step=b"1",  required=True, value=b"%d"%year, event=b'onchange="this.form.submit()"'),
                        ], class_=b'col-md-2'),
                        Div(
                        [
                                Select(
                                [
                                        Option(text=electricmeter.lang.type_price, value=b"price", selected= True if unit == b"price" else False),
                                        Option(text=electricmeter.lang.type_power, value=b"power", selected= True if unit == b"power" else False),
                                ], spacer=b"", text=electricmeter.lang.step_minutes, name=b"unit",                 event=b'onchange="this.form.submit()"')
                        ],class_=b'col-md-2')
                ], class_=b"row"),

                Tag(content%(b"monthly", 86400, tools.date.date_to_html(month), tools.lang.translate_date(month, False), unit, electricmeter.lang.power_consumed, geolocation.latitude, geolocation.longitude, b""))
        ]
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_electricmeter + electricmeter.lang.item_month.lower(),Form(page_content))
        await response.send_page(page)
async def power_page_datas(request, response, args)

Send current power consumed

Expand source code
@server.httpserver.HttpServer.add_route(b'/power_datas')
async def power_page_datas(request, response, args):
        """ Send current power consumed """
        try:
                power = {"power":electricmeter.meter.HourlyCounter.get_power()}
                await response.send_buffer(b"pulses", buffer=tools.strings.tobytes(json.dumps(power)), mime_type=b"application/json")
        except Exception as err:
                await response.send_not_found(err)
async def rate_page(request, response, args)

Electric rate configuration page

Expand source code
@server.httpserver.HttpServer.add_route(b'/rate', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_rate)
async def rate_page(request, response, args):
        """ Electric rate configuration page """
        rates   = electricmeter.config.RatesConfig.get_config()
        current = electricmeter.config.RateConfig()

        # If new rate added
        if request.params.get(b"add",None) is not None:
                current.update(request.params, show_error=False)
                rates.append(current)
                rates.save()
        # If a rate must be deleted
        elif request.params.get(b"remove",None) is not None:
                rates.remove(request.params.get(b"remove",b"none"))
                rates.save()
        # If a rate must be edited
        elif request.params.get(b"edit",None) is not None:
                current = rates.get(request.params.get(b"edit",b"none"))

        # List all rates
        rate_items = []
        identifier = 0
        while True:
                rate = rates.get(identifier)
                if rate is not None:
                        rate_items.append(
                                        ListItem(
                                        [
                                                Link(text=b" %s : %f %s %s %s "%(rate.name, rate.price, rate.currency, electricmeter.lang.from_the, tools.lang.translate_date(rate.validity_date)), href=b"rate?edit=%d"%identifier ),
                                                Link(text=electricmeter.lang.remove_button , class_=b"btn position-absolute top-50 end-0 translate-middle-y", href=b"rate?remove=%d"%identifier, onclick=b"return window.confirm('%s')"%electricmeter.lang.remove_dialog)
                                        ]))
                else:
                        break
                identifier += 1

        # Build page
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_rate,
        [
                Form(
                [
                        Edit  (text=electricmeter.lang.name,          name=b"name",          placeholder=electricmeter.lang.field_rate,     required=True, value=current.name),
                        Edit  (text=electricmeter.lang.validy_date,   name=b"validity_date", type=b"date",                    required=True, value=b"%04d-%02d-%02d"%tools.date.local_time(current.validity_date)[:3]),
                        Edit  (text=electricmeter.lang.price,         name=b"price",         type=b"number", step=b"0.0001",  required=True, value=b"%f"%current.price),
                        Edit  (text=electricmeter.lang.currency,      name=b"currency",      placeholder=electricmeter.lang.field_currency, required=True, value=current.currency),
                        Submit(text=electricmeter.lang.add_button,    name=b"add")
                ]),
                List(rate_items)
        ])
        await response.send_page(page)
async def time_slots_page(request, response, args)

Electric time slots configuration page

Expand source code
@server.httpserver.HttpServer.add_route(b'/time_slots', menu=electricmeter.lang.menu_electricmeter, item=electricmeter.lang.item_time_slots)
async def time_slots_page(request, response, args):
        """ Electric time slots configuration page """
        time_slots = electricmeter.config.TimeSlotsConfig.get_config()
        rates   = electricmeter.config.RatesConfig.get_config()
        current = electricmeter.config.TimeSlotConfig()

        # If new time_slot added
        if request.params.get(b"add",None) is not None:
                current.update(request.params, show_error=False)
                time_slots.append(current)
                time_slots.save()
        # If a time_slot must be deleted
        elif request.params.get(b"remove",None) is not None:
                time_slots.remove(request.params.get(b"remove",b"none"))
                time_slots.save()
        # If a time_slot must be edited
        elif request.params.get(b"edit",None) is not None:
                current = time_slots.get(request.params.get(b"edit",b"none"))

        # List all rates names
        rates_combo = []
        names       = {}
        identifier  = 0
        while True:
                rate = rates.get(identifier)
                if rate is not None:
                        if rate.name not in names:
                                rates_combo.append(Option(text=rate.name, value=rate.name, selected=b"selected" if current.rate == rate.name else b""))
                else:
                        break
                names[rate.name] = b""
                identifier += 1

        # List all rates
        time_slots_items = []
        identifier = 0
        while True:
                time_slot = time_slots.get(identifier)
                if time_slot is not None:
                        time_slots_items.append(
                                        ListItem(
                                        [
                                                Label(text=b"    ", style=b"background-color: %s"%time_slot.color),
                                                Space(),Space(),
                                                Link(text=b"%s - %s : %s "%(tools.date.time_to_html(time_slot.start_time), tools.date.time_to_html(time_slot.end_time), time_slot.rate),href=b"time_slots?edit=%d"%identifier),
                                                Link(text=electricmeter.lang.remove_button , class_=b"btn position-absolute top-50 end-0 translate-middle-y", href=b"time_slots?remove=%d"%identifier, onclick=b"return window.confirm('%s')"%electricmeter.lang.remove_dialog)
                                        ]))
                else:
                        break
                identifier += 1

        # Build page
        page = webpage.mainpage.main_frame(request, response, args, electricmeter.lang.title_time_slots,
        [
                Form(
                [
                        Edit (text=electricmeter.lang.field_start,      name=b"start_time", type=b"time", required=True, value=tools.date.time_to_html(current.start_time)),
                        Edit (text=electricmeter.lang.field_end,        name=b"end_time",   type=b"time", required=True, value=tools.date.time_to_html(current.end_time)),
                        Label(text=electricmeter.lang.field_time_rate),
                        Select(rates_combo,name=b"rate",                                    required=True),
                        Edit (text=electricmeter.lang.field_color,      name=b"color", type=b"color",     required=True, value=current.color),
                        Submit(text=electricmeter.lang.add_button,      name=b"add")
                ]),
                List(time_slots_items)
        ])
        await response.send_page(page)