PRICE-01 round 2: the token on no page, fixed-tariff note, token length, F3 words, test gaps
F1: every password option is shown as ******** (set) or empty in /config/model (options, running, ui) and in a stale refusal; the classic page draws it as a password field; a save or validate that sends ******** or nothing keeps the stored value. F2: a static_profile beside price_service is said once at start (notification and log), not refused. F3 (owner, 2026-10-14): the no-price row, Home line and notification only while maintenance_enabled is on. F4: tests for the reviewer's R01 and R10. F5: price_service_token must be 16+ characters. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
This commit is contained in:
co-authored by
Claude
parent
4eb88f7fda
commit
0801776645
@@ -90,7 +90,8 @@ from .p1 import P1Ingest, build_source, is_enabled
|
||||
from .price import Absence, Uncertainty, brussels, price_option, provider_configured
|
||||
from .price import required_spread as price_required_spread
|
||||
from .price import uncertainty_of as price_uncertainty_of
|
||||
from .price_chain import PriceWatch, gate_for, uses_chain
|
||||
from .price_chain import (PriceWatch, fixed_tariff_unused, gate_for,
|
||||
uses_chain)
|
||||
from .price_history import PriceHistory
|
||||
from .static_tariff import resolve_tariff, static_gated, static_status
|
||||
from .tariff import PeakStore, PeakTracker, next_quarter
|
||||
@@ -1213,6 +1214,7 @@ class Controller:
|
||||
self.price_gap = False
|
||||
self.price_gap_since = None
|
||||
self._price_gap_announced = False
|
||||
self._fixed_unused_said = False
|
||||
self._gated_key = None # MAINT-13: (options, hour) of the cache
|
||||
# TARIFF-03. One line of prose for the status page saying WHICH tariff
|
||||
# window we are in and why it is or is not cheap - so "Cheap window:
|
||||
@@ -4030,7 +4032,10 @@ class Controller:
|
||||
gate = gate_for(published, now)
|
||||
self.cheap, self._price_gated = gate.cheap, True
|
||||
self.tariff_words = gate.words
|
||||
self.price_gap = (not gate.covered) and self.tariff_at is not None
|
||||
# Owner, 2026-10-14: the words say 'maintenance waits', so they show
|
||||
# only while the maintenance charge is on.
|
||||
self.price_gap = ((not gate.covered) and self.tariff_at is not None
|
||||
and self.o.get("maintenance_enabled") is True)
|
||||
last = self._last_published
|
||||
if gate.covered or last is None:
|
||||
self.price_gap_since = None
|
||||
@@ -5586,6 +5591,7 @@ class Controller:
|
||||
# Not a safety check, so it is one of the cheap ones: an attribute test
|
||||
# and, on a change only, one Home Assistant call.
|
||||
await self.check_price_gap()
|
||||
await self.check_fixed_unused()
|
||||
|
||||
# --- SAFETY-31's alarm, and its boundary is the LATCH, not a number ---
|
||||
# Unconstrained where the two above are not: it grades
|
||||
@@ -6223,6 +6229,19 @@ class Controller:
|
||||
await self.hass.call("persistent_notification", "dismiss",
|
||||
messages.prices_back().data())
|
||||
|
||||
async def check_fixed_unused(self) -> None:
|
||||
"""PRICE-01 F2. A `static_profile` beside `price_service` no longer
|
||||
drives the cheap window or the maintenance gate. Not refused: said once,
|
||||
at the first cycle, in the log and as one notification."""
|
||||
if self._fixed_unused_said:
|
||||
return
|
||||
self._fixed_unused_said = True
|
||||
if fixed_tariff_unused(self.o):
|
||||
_LOG.warning("The fixed tariff is not used, because the price "
|
||||
"service gives the prices.")
|
||||
await self.hass.call("persistent_notification", "create",
|
||||
messages.fixed_tariff_unused().data())
|
||||
|
||||
def _gap_args(self) -> tuple:
|
||||
"""`(since, now)` in Brussels time for the gap's words. `since` is None
|
||||
when no price was ever published."""
|
||||
|
||||
Reference in New Issue
Block a user