Skip to content

Add consumption-weighted average price sensors ("your own average") - #108

Draft
enoch85 wants to merge 3 commits into
mainfrom
feature/consumption-weighted-average
Draft

Add consumption-weighted average price sensors ("your own average")#108
enoch85 wants to merge 3 commits into
mainfrom
feature/consumption-weighted-average

Conversation

@enoch85

@enoch85 enoch85 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

What & why

Today you can see the market Average Price but not whether your own usage beat it. Beating the average means using more electricity during below-average-price intervals — only measurable by weighting each interval's price by the energy you actually consumed.

This adds an opt-in consumption-weighted average: two sensors per area — Average Price Paid Today and Average Price Paid This Month — created only when you pick a cumulative kWh energy sensor in the integration options.

How it works

  • Each meter increment Δkwh is priced at the current interval's all-in price: cost += Δkwh × price, energy += Δkwh; the sensor state is cost / energy (same unit as Average Price).
  • Benchmark = the simple (unweighted) average of the spot price over the same elapsed intervals. If your weighted average is below it, you beat the market — surfaced as savings_vs_average and beating_average attributes.
  • Resets at local midnight (daily) / month start (monthly); accumulators persist across restarts via RestoreEntity with a config fingerprint (unit/VAT/currency/meter change → clean restart). The meter baseline re-seeds on startup so downtime isn't mispriced; meter resets/replacements are handled.

Files

  • sensor/consumption.py — pure, hass-free WeightedAverageAccumulator (the testable core)
  • sensor/price.pyConsumptionWeightedAverageSensor (RestoreEntity adapter)
  • sensor/electricity.py — opt-in wiring
  • config_flow/schemas.py + const/ + translations/ — the energy-sensor option
  • tests/pytest/unit/ — accumulator math (incl. a simulated month) + sensor adapter

Testing

  • Full unit suite green (490) + 16 new tests, incl. a simulated month asserting weighted < simple with load-shifted consumption.
  • Verified live in Home Assistant end-to-end (config flow → sensor creation → simulated consumption): per-interval math, cumulative accumulation, meter-reset re-baselining, and cross-interval beating all matched hand-computed values exactly.

Draft: opening for review of the approach/naming before finalizing.

enoch85 added 3 commits July 1, 2026 17:46
Lets users see whether they beat the market average by weighting each
interval's spot price by the energy they actually consumed. Opt-in: two
sensors per area (today + this month) are created only when a cumulative
kWh energy sensor is selected in the options.

- New pure WeightedAverageAccumulator (sensor/consumption.py): cost/energy
  and simple-benchmark accumulation, period reset, meter-reset handling.
- ConsumptionWeightedAverageSensor (RestoreEntity): tracks the meter via
  state-change events, samples the elapsed-interval simple average as the
  benchmark, persists across restarts with a config fingerprint, resets at
  local midnight (daily) / month start (monthly).
- Options: optional energy-sensor EntitySelector (domain sensor,
  device_class energy) + translations (en/de/nl/strings).
- Attributes: simple_average, savings_vs_average, beating_average,
  consumed_energy, accumulated_cost, period, period_start.
- Tests: pure accumulator math incl. a simulated month, and the sensor
  adapter (opt-in gating, restore/fingerprint, attributes, availability).
Sorts next to the market Average Price sensor in entity pickers and reads
naturally: what you paid vs what the market averaged.

- sensor_type: consumption_weighted_average_today/month
  -> average_price_paid_today/month
- Names: "Your Average Price Today/This Month"
  -> "Average Price Paid Today/This Month"

Unreleased (draft PR), so renamed directly without aliases.
If the energy meter entity was still unavailable when the sensor was added
(startup race), the baseline seed found no numeric state and the first meter
increment was swallowed as a baseline reading, losing that consumption.
Seed from the state-change event's old_state instead so the delta books.

Caught by the live week-long verification (a run right after boot booked
0 kWh while the independent reference booked 2 kWh).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant