UX-08 2d: plain log lines, the rest of the status page, and DOCS
Every WARNING and ERROR line is one plain line of at most 25 words, with no card ids, capitals or semicolons; the reasoning of the ten longest is on a DEBUG line. The ux08 allowlist is empty and its cap is 0 (43 -> 0). The status page: the month-peak and peak-guard rows, the stale-reading row, the top table (Why, Tariff, Arbitrage, Shadow mode, labels), the Stopped banner and the line under the buttons, which now shows the plain hint of each event instead of the raw action log. The Supervisor question at start logs one line with a real reason. Refusals are counted as shown (labels expanded, long cases driven), seven were shortened, the card and parse errors lost their internals, a blank value reads "empty", and the panel no longer files inverters.max_w under the flat max_w. Option names lose "(JSON)", the NL texts are checked too. DOCS gets a glossary (control board, isolator, Developer Tools) and card ids move from headings to a "For installers" line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p
This commit is contained in:
@@ -472,7 +472,7 @@ class P1Ingest:
|
||||
"""
|
||||
self.parse_errors += 1
|
||||
self.last_error = str(err)
|
||||
_LOG.warning("P1 telegram rejected: %s", err)
|
||||
_LOG.warning("Meter reading refused: %s", err)
|
||||
|
||||
# -- what consumers read -------------------------------------------------
|
||||
def age_s(self) -> float | None:
|
||||
@@ -1167,7 +1167,7 @@ def validate_meter_options(opts: dict, phases: int, max_age_s: float) -> list[st
|
||||
problems.append(
|
||||
f"{_S('meter_source')} is {SOURCE_HA!r}, so it needs "
|
||||
f"{_S('p1_import_entity')} and {_S('p1_export_entity')}. They "
|
||||
f"are {imp or '<blank>'!r} and {exp or '<blank>'!r}.")
|
||||
f"are {messages.shown(imp)} and {messages.shown(exp)}.")
|
||||
elif source == SOURCE_HA_SIGNED:
|
||||
net = str(opts.get("p1_net_entity", "") or "").strip()
|
||||
phase_net = [str(e).strip() for e in
|
||||
@@ -1194,7 +1194,7 @@ def validate_meter_options(opts: dict, phases: int, max_age_s: float) -> list[st
|
||||
try:
|
||||
int(port_s)
|
||||
except ValueError:
|
||||
problems.append(f"{_S('p1_host')} is {opts.get('p1_host')!r}, and "
|
||||
problems.append(f"{_S('p1_host')} is {messages.shown(opts.get('p1_host'))}, and "
|
||||
"its port is not a number.")
|
||||
try:
|
||||
# ⚠️ Not `or 5`: that turns an explicit 0 into the default, and a
|
||||
@@ -1204,7 +1204,7 @@ def validate_meter_options(opts: dict, phases: int, max_age_s: float) -> list[st
|
||||
poll_s = float(5 if raw is None or raw == "" else raw)
|
||||
except (TypeError, ValueError):
|
||||
problems.append(f"{_S('meter_poll_s')} is "
|
||||
f"{opts.get('meter_poll_s')!r}, which is not a "
|
||||
f"{messages.shown(opts.get('meter_poll_s'))}, which is not a "
|
||||
"number.")
|
||||
else:
|
||||
if poll_s <= 0:
|
||||
@@ -1269,9 +1269,8 @@ def build_source(opts: dict, ingest: P1Ingest, session, broker: dict | None):
|
||||
# 0 W cycle, which is fail-safe. The refusal above is for cadences that
|
||||
# can NEVER be fresh.
|
||||
if poll_s > ingest.max_age_s / 2:
|
||||
_LOG.warning("meter_poll_s %.1f leaves no room under meter_max_age_s "
|
||||
"%.1f: one missed poll makes the reading stale. The meter "
|
||||
"updates every ~5 s; 5 s against 30 s is the tested pair.",
|
||||
_LOG.warning("“Time between meter reads” (%.1f s) is too close to "
|
||||
"“Reading too old after” (%.1f s). Use 5 s and 30 s.",
|
||||
poll_s, ingest.max_age_s)
|
||||
return HomeWizardLocalSource(session, ingest, host, port=port, poll_s=poll_s)
|
||||
if source == SOURCE_MQTT:
|
||||
|
||||
Reference in New Issue
Block a user