Deploy 0.18.1 to master #197

Merged
admin merged 9 commits from deploy-0.18.1 into master 2026-10-16 15:05:07 +02:00
6 changed files with 76 additions and 15 deletions
Showing only changes of commit b703aaa6d5 - Show all commits
+3 -2
View File
@@ -3120,8 +3120,9 @@ configuration rather than "turn the price feature off and hope".
On the add-on page, choose **Other time windows** under Settings, Tariff and
prices, or in Setup step 4. Add one row per time window, with its start, end,
days and price. The page names any overlap, gap or empty price before you can
save. You only need the JSON below for days the page cannot choose, such as
Monday and Wednesday only.
save. Prices take five decimals: divide the c€/kWh on your bill by 100, so
34.512 c€/kWh is 0.34512 €/kWh. You only need the JSON below for days the page
cannot choose, such as Monday and Wednesday only.
It is one option, holding a JSON object, on one line:
+1 -1
View File
@@ -311,7 +311,7 @@
const days = h("select", { class: "sel", "aria-label": say(P.days) });
for (const [v, label] of f.days || []) days.append(h("option", { value: v, selected: v === row.days }, label));
days.addEventListener("change", () => set("days", days.value));
const price = h("input", { class: "num", type: "number", inputmode: "decimal", step: "0.01",
const price = h("input", { class: "num", type: "number", inputmode: "decimal", step: String(f.step || 0.01),
value: row.price == null ? "" : row.price, "aria-label": say(P.price) });
price.addEventListener("input", () => set("price", price.value === "" || isNaN(+price.value) ? null : +price.value));
const cell = (label, ...kids) => h("label", { class: "pf" }, h("span", { class: "tiny muted" }, label), ...kids);
+4 -4
View File
@@ -23,10 +23,10 @@
"contract": {"name": "Your contract", "description": "How your supplier prices each kWh you take from the grid."},
"region": {"name": "Region", "description": "Fills in grid costs, levies and VAT for the year shown, but not yet for Wallonia or Brussels, so check your bill."},
"fallback": {"name": "Price when the feed is down", "description": "Used when the price source does not answer."},
"fixed_price": {"name": "Price per kWh, all in", "description": "Energy, supplier costs, grid costs, levies and VAT together."},
"day_price": {"name": "Day price, weekdays 07:00 to 22:00", "description": "All in, as on your bill."},
"night_price": {"name": "Night and weekend price", "description": "All in, as on your bill."},
"periods": {"name": "Time windows and prices", "description": "One price per kWh for each time window, all in, as on your bill."}
"fixed_price": {"name": "Price per kWh, all in", "description": "Energy, supplier costs, grid costs, levies and VAT together: divide c€/kWh from the bill by 100, so 34.512 c€/kWh is 0.34512."},
"day_price": {"name": "Day price, weekdays 07:00 to 22:00", "description": "All in, as on your bill: divide c€/kWh by 100, so 34.512 c€/kWh is 0.34512."},
"night_price": {"name": "Night and weekend price", "description": "All in, as on your bill: divide c€/kWh by 100, so 34.512 c€/kWh is 0.34512."},
"periods": {"name": "Time windows and prices", "description": "One price per kWh for each time window, all in, as on your bill: divide c€/kWh by 100, so 34.512 c€/kWh is 0.34512."}
},
"choices": {
"contract": {"none": "No prices", "fixed": "Fixed price", "daynight": "Day and night", "dynamic": "Dynamic, hourly", "custom": "Other time windows"},
+4 -4
View File
@@ -23,10 +23,10 @@
"contract": {"name": "Uw contract", "description": "Hoe uw leverancier elke kWh prijst die u van het net afneemt."},
"region": {"name": "Regio", "description": "Vult nettarieven, heffingen en btw in voor het getoonde jaar, nog niet voor Wallonië of Brussel, dus kijk uw factuur na."},
"fallback": {"name": "Prijs als de feed wegvalt", "description": "Gebruikt als de prijsbron niet antwoordt."},
"fixed_price": {"name": "Prijs per kWh, alles inbegrepen", "description": "Energie, kosten van de leverancier, nettarieven, heffingen en btw samen."},
"day_price": {"name": "Dagprijs, weekdagen 07:00 tot 22:00", "description": "Alles inbegrepen, zoals op uw factuur."},
"night_price": {"name": "Nacht- en weekendprijs", "description": "Alles inbegrepen, zoals op uw factuur."},
"periods": {"name": "Tijdvakken en prijzen", "description": "Eén prijs per kWh voor elk tijdvak, alles inbegrepen, zoals op uw factuur."}
"fixed_price": {"name": "Prijs per kWh, alles inbegrepen", "description": "Energie, kosten van de leverancier, nettarieven, heffingen en btw samen: deel c€/kWh van de factuur door 100, dus 34,512 c€/kWh is 0,34512."},
"day_price": {"name": "Dagprijs, weekdagen 07:00 tot 22:00", "description": "Alles inbegrepen, zoals op uw factuur: deel c€/kWh door 100, dus 34,512 c€/kWh is 0,34512."},
"night_price": {"name": "Nacht- en weekendprijs", "description": "Alles inbegrepen, zoals op uw factuur: deel c€/kWh door 100, dus 34,512 c€/kWh is 0,34512."},
"periods": {"name": "Tijdvakken en prijzen", "description": "Eén prijs per kWh voor elk tijdvak, alles inbegrepen, zoals op uw factuur: deel c€/kWh door 100, dus 34,512 c€/kWh is 0,34512."}
},
"choices": {
"contract": {"none": "Geen prijzen", "fixed": "Vaste prijs", "daynight": "Dag en nacht", "dynamic": "Dynamisch, per uur", "custom": "Andere tijdvakken"},
+4 -4
View File
@@ -164,13 +164,13 @@ GROUPS = (
wizard=4),
Field("price_export_formula", TEXT),
Field("ui.fallback", SEGMENTED, show=_DYNAMIC, wizard=4),
Field("ui.fixed_price", NUMBER, "€/kWh", step=0.01,
Field("ui.fixed_price", NUMBER, "€/kWh", step=0.00001,
show=(("ui.fallback", "==", "fixed"),), wizard=4),
Field("ui.day_price", NUMBER, "€/kWh", step=0.01,
Field("ui.day_price", NUMBER, "€/kWh", step=0.00001,
show=(("ui.fallback", "==", "daynight"),), wizard=4),
Field("ui.night_price", NUMBER, "€/kWh", step=0.01,
Field("ui.night_price", NUMBER, "€/kWh", step=0.00001,
show=(("ui.fallback", "==", "daynight"),), wizard=4),
Field("ui.periods", PERIOD_LIST, "€/kWh", step=0.01,
Field("ui.periods", PERIOD_LIST, "€/kWh", step=0.00001,
show=(("ui.fallback", "==", "custom"),), wizard=4),
)),
Part("selling", (
+60
View File
@@ -327,6 +327,66 @@ check("days the editor has no choice for stay in the Configuration tab, and "
["ui.periods"] is None and edit(dict(SITE, static_profile=_raw), gain=0.5)
["static_profile"] == _raw)
print("\n8. UX-43: tariff prices take five decimals")
from app import uilayout as _L # noqa: E402
_PX = 0.34512
_TARIFF = ("ui.fixed_price", "ui.day_price", "ui.night_price", "ui.periods")
_steps = {k: _L.field(k).step for k in _TARIFF}
check("the four tariff price fields step 0.00001 EUR/kWh",
sorted(_steps) == sorted(_TARIFF)
and all(v == 0.00001 for v in _steps.values()))
_f5 = edit(DEFAULTS, ui__contract="fixed", ui__fixed_price=_PX)
check("fixed price 0.34512 is saved exactly and read back exactly",
'"eur_per_kwh": 0.34512}' in _f5["static_profile"]
and U.options_to_ui(_f5)["ui.fixed_price"] == _PX
and trip(_f5) == _f5)
_d5 = edit(SITE, ui__contract="daynight", ui__day_price=_PX,
ui__night_price=0.26789)
check("day price 0.34512 and night price 0.26789 are saved and read back exactly",
'"eur_per_kwh": 0.34512' in _d5["static_profile"]
and '"eur_per_kwh": 0.26789' in _d5["static_profile"]
and U.options_to_ui(_d5)["ui.day_price"] == _PX
and U.options_to_ui(_d5)["ui.night_price"] == 0.26789
and not validate_startup_config(_d5))
_n5 = edit(SITE, ui__contract="daynight", ui__day_price=0.3, ui__night_price=_PX)
check("night price 0.34512 alone is saved and read back exactly",
U.options_to_ui(_n5)["ui.night_price"] == _PX)
_r3 = rows(("00:00", "07:00", "mon-sun", 0.12345), ("07:00", "22:00", "mon-sun", _PX),
("22:00", "24:00", "mon-sun", 0.05001))
_p3 = periods(_r3)
check("three windows with five-decimal prices save and read back exactly",
[p.eur_per_kwh for p in parse_static_profile(_p3["static_profile"]).periods]
== [0.12345, _PX, 0.05001]
and [r["price"] for r in U.options_to_ui(_p3)["ui.periods"]]
== [0.12345, _PX, 0.05001] and trip(_p3) == _p3
and "e-" not in _p3["static_profile"])
check("a two-decimal price still works", U.options_to_ui(
edit(DEFAULTS, ui__contract="fixed", ui__fixed_price=0.31))
["ui.fixed_price"] == 0.31)
_old = dict(SITE, price_provider="off", static_profile=json.dumps({"periods": [
{"name": "day", "from": "07:00", "to": "22:00", "days": "mon-fri",
"eur_per_kwh": 0.34},
{"name": "night", "from": "22:00", "to": "07:00", "days": "mon-fri",
"eur_per_kwh": 0.26},
{"name": "night", "from": "00:00", "to": "24:00", "days": "sat,sun",
"eur_per_kwh": 0.26}]}))
check("a stored profile with two-decimal prices is unchanged by load and save",
trip(_old) == _old)
_js = open(os.path.join(HERE, "app", "static", "settings.js"),
encoding="utf-8").read()
check("the window editor's price input takes its step from the field, not a "
"hard-coded 0.01", 'step: "0.01"' not in _js
and "step: String(f.step || 0.01)" in _js)
for _lang in ("en", "nl"):
with open(os.path.join(HERE, "app", "ui_text", f"{_lang}.json"),
encoding="utf-8") as _fh:
_flds = json.load(_fh)["fields"]
_hint = "34.512" if _lang == "en" else "34,512"
_eq = "0.34512" if _lang == "en" else "0,34512"
check(f"[{_lang}] the c€/kWh hint sits on all four tariff price fields",
all(_hint in _flds[k]["description"] and _eq in _flds[k]["description"]
for k in ("fixed_price", "day_price", "night_price", "periods")))
print()
if fails:
print(f"{len(fails)} FAILED")