feat: publish marginal cost matrix to predbat gateway#3783
feat: publish marginal cost matrix to predbat gateway#3783springfall2008 merged 9 commits intomainfrom
Conversation
Extends the predbat_data MQTT payload with the marginal cost matrix
produced by the Marginal mixin. The gateway's range display uses
this to colour appliance RAG indicators based on the real cost of
running each appliance now vs in upcoming time windows — replacing
the slot-category heuristic that couldn't distinguish a 100W trickle
from a full 3kW solar covering the load.
Payload additions:
marginal_costs — 4×N matrix, rows are 1/2/4/8 kWh extra load,
columns are the time windows from marginal.py
marginal_time_labels — HH:MM labels for each column
Missing/failed reads publish empty lists, so older PredBat versions
without marginal.py are handled gracefully on the gateway side.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review feedback — three issues in the first cut: 1. CRITICAL: attribute="all" is not supported by ha.py get_state(); it would look up an attribute literally named "all" which doesn't exist, so the read silently returned None and marginal_costs was always empty in the payload. Use attribute="matrix" directly — marginal.py publishes the full matrix as that named attribute. 2. Narrow the exception catch to (TypeError, ValueError, AttributeError, KeyError) instead of the blanket Exception, per the recent reviewer feedback pattern on #3725. 3. Align warning log with the file's established "Warn: GatewayMQTT:" convention (every other warning in the file uses that prefix). Also removes the defensive two-level unwrap and the nested _row helper (both were cargo-culted around the broken read; now unnecessary). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code reviewFound 1 critical issue, already addressed in b8bf2fe:
https://github.com/springfall2008/batpred/blob/b8bf2fe9/apps/predbat/gateway.py#L858-L898 Same commit also:
Not actioned but noted for the reviewer:
🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
6 new tests on TestPublishPredbatData: - test_marginal_costs_nominal_matrix: int-keyed 4×3 matrix flattens in canonical 1/2/4/8 order. - test_marginal_costs_string_keys_work: string-keyed matrix (JSON round-trip) produces identical output. - test_marginal_costs_missing_sensor_empty_lists: missing sensor publishes empty lists (fallback path the gateway firmware relies on for its own RAG fallback). - test_marginal_costs_missing_row_padded_with_zeros: one absent level pads with 0 rather than collapsing the whole matrix. - test_marginal_costs_non_numeric_value_caught: non-numeric cells (e.g. "N/A") are caught by the narrow except and fall back to empty, rather than raising. - test_marginal_costs_non_dict_matrix_ignored: malformed sensor (list instead of dict) ignored cleanly. All 126 gateway tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Added test coverage in 0f9860c — 6 new tests on |
There was a problem hiding this comment.
Pull request overview
Extends the GatewayMQTT._publish_predbat_data MQTT payload to include PredBat’s marginal energy cost matrix so the ESP32 gateway can render appliance RAG indicators based on actual marginal cost rather than slot category heuristics.
Changes:
- Publish
marginal_costs(4×N matrix) andmarginal_time_labelsalongside existingpredbat_datafields. - Add gateway-side flattening/normalization logic to handle int vs string keys and missing/invalid data.
- Add unit tests covering nominal, string-keyed, missing-sensor, missing-row padding, and invalid-matrix fallback behaviors.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| apps/predbat/gateway.py | Adds marginal matrix extraction/flattening and includes it in the MQTT predbat_data payload. |
| apps/predbat/tests/test_gateway.py | Adds tests validating the new marginal payload fields and key fallback behaviors. |
| # Skip rows that don't match the established column shape. | ||
| if time_labels and any(tl not in row for tl in time_labels): | ||
| # Missing columns — pad with 0 rather than dropping the row. | ||
| tmp_costs.append([round(float(row.get(tl, 0) or 0), 2) for tl in time_labels]) | ||
| else: | ||
| tmp_costs.append([round(float(row.get(tl, 0) or 0), 2) for tl in time_labels]) |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Extends the ESP32 Gateway predbat_data MQTT payload to include PredBat’s marginal energy cost matrix (4 load levels × upcoming time windows) so the gateway firmware can color appliance indicators based on actual marginal cost rather than slot categories.
Changes:
- Add marginal cost matrix extraction/flattening to
GatewayMQTT._publish_predbat_data()and publish it asmarginal_costs+marginal_time_labels. - Add unit tests covering nominal, missing, malformed, and partially missing marginal matrix inputs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
apps/predbat/gateway.py |
Adds marginal matrix read/flatten logic and includes it in the predbat_data payload. |
apps/predbat/tests/test_gateway.py |
Adds tests validating the published marginal payload structure and fallbacks. |
| "predbat.rates": "10.0", | ||
| "predbat.cost_today": "0", | ||
| "predbat.ppkwh_today": "10.0", | ||
| "predbat.marginal_energy_costs#matrix": matrix, | ||
| } |
| "predbat.rates": "10.0", | ||
| "predbat.cost_today": "0", | ||
| "predbat.ppkwh_today": "10.0", | ||
| "predbat.marginal_energy_costs#matrix": matrix, | ||
| } |
| "predbat.rates": "10.0", | ||
| "predbat.cost_today": "0", | ||
| "predbat.ppkwh_today": "10.0", | ||
| "predbat.marginal_energy_costs#matrix": matrix, | ||
| } |
| "predbat.rates": "10.0", | ||
| "predbat.cost_today": "0", | ||
| "predbat.ppkwh_today": "10.0", | ||
| "predbat.marginal_energy_costs#matrix": matrix, | ||
| } |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR extends the MQTT predbat_data payload produced by GatewayMQTT._publish_predbat_data() to include PredBat’s marginal energy cost matrix (and corresponding time labels) so the ESP32 gateway can render appliance RAG indicators based on real marginal p/kWh impact rather than inferred slot categories.
Changes:
- Add
marginal_costs(4×N list) andmarginal_time_labels(N labels) to the gateway MQTT payload, sourced fromsensor.{prefix}_marginal_energy_costsattributematrix. - Add gateway publish-path tests covering nominal matrix flattening, string/int keys, missing rows padding, and malformed data fallback behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
apps/predbat/gateway.py |
Reads the marginal cost sensor matrix and publishes it as JSON lists in the gateway MQTT payload with defensive fallbacks. |
apps/predbat/tests/test_gateway.py |
Adds unit tests validating payload formatting and error handling for the marginal cost matrix fields. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Summary
Extends the
predbat_dataMQTT payload sent to the ESP32 gateway device (GatewayMQTT._publish_predbat_data) with the marginal cost matrix produced by theMarginalmixin (apps/predbat/marginal.py).The gateway's range display uses this to decide the colour of its appliance RAG indicators (oven, dryer, wash, EV). Previously the firmware had to infer cost from slot categories, which was misleading — e.g. a SOLAR slot with 100W forecast PV vs 3kW would both look GREEN for the dryer. Sending the actual marginal matrix lets the firmware show the real cost impact.
Payload additions
{ "marginal_costs": [ [5.2, 4.1, 3.8, 6.0, 8.0, 7.0, 6.5], [5.8, 4.3, 3.9, 6.2, 8.5, 7.2, 6.7], [8.1, 7.5, 6.8, 9.0, 11.0, 10.0, 9.5], [12.3, 11.5, 10.8, 13.5, 16.0, 15.0, 14.0] ], "marginal_time_labels": ["14:00", "16:00", "18:00", "20:00", "22:00", "00:00", "02:00"] }Rows are the 4
MARGINAL_EXTRA_KWH_LEVELS(1/2/4/8 kWh). Columns are the 7MARGINAL_TIME_OFFSETS. Values are p/kWh, same as the existing HA sensor attribute.Fallback behaviour
Related
Predictive-Cloud-Ltd/predbat-gateway#41(feat: Range display)apps/predbat/marginal.py(no changes)