Fetches and parses Luminus "Comfy" electricity price-list PDFs for a double-tariff (day/night) meter. Two ways to use it:
- CLI script (
luminus_price.py) — text/JSON output, or publish to MQTT with Home Assistant auto-discovery. - Native Home Assistant integration (
custom_components/luminus_price/, installable via HACS) — a proper config-flow integration that polls Luminus directly and exposes the same 5 sensors per configured region, no MQTT broker required.
Luminus doesn't publish these prices as HTML/JSON — the price list page
(https://www.luminus.be/nl/prive/energie/prijslijsten/) serves a per-plan,
per-region PDF via api-next/get-pricelist. This script downloads that
PDF and extracts the "Tweevoudige meter" (double tariff) table: peak/
off-peak consumption prices, peak/off-peak injection (solar feed-in)
prices, and the annual fixed fee.
pip install -r requirements.txtpaho-mqtt is only needed for --format mqtt.
# Human-readable, Flanders (default region)
python3 luminus_price.py
# Another region
python3 luminus_price.py --region wallonia
# JSON
python3 luminus_price.py --format json
# Publish to an MQTT broker with Home Assistant discovery
python3 luminus_price.py --format mqtt --mqtt-host 192.168.1.10 \
--mqtt-username myuser --mqtt-password mypass
# Historical: the price a contract signed in January 2024 started at
python3 luminus_price.py --signing-month 2024-01Only one region is fetched per run. To track more than one region in
Home Assistant, run the script once per region — the default MQTT base
topic already encodes the region (luminus/<region>), so separate runs
don't collide:
python3 luminus_price.py --region flanders --format mqtt --mqtt-host 192.168.1.10
python3 luminus_price.py --region wallonia --format mqtt --mqtt-host 192.168.1.10flanders, wallonia, brussels.
Without --signing-month: 6 retained messages — 5 discovery configs
(consumption_peak_ckwh, consumption_off_peak_ckwh,
injection_peak_ckwh, injection_off_peak_ckwh, fixed_fee_eur_year)
plus 1 JSON state topic.
With --signing-month: 10 retained messages — the same 5 (now sourced
from the contract-vs-current comparison's "current" values) plus 4 more
(contract_consumption_peak_ckwh, contract_consumption_off_peak_ckwh,
contract_injection_peak_ckwh, contract_injection_off_peak_ckwh) —
your contract's original signing-month prices, for comparison. All 9/5
share one state topic and one HA "device" entry.
Discovery configs live at
<discovery-prefix>/sensor/luminus_comfy_<topic-slug>_<key>/config
(default discovery prefix: homeassistant); state at
<mqtt-base-topic>/state. Both are keyed off --mqtt-base-topic, not
the region — the region only shows in the device's display name (e.g.
"Luminus Comfy Electricity (Flanders)").
Relevant flags: --mqtt-host (default localhost), --mqtt-port
(1883), --mqtt-username/--mqtt-password, --mqtt-tls,
--mqtt-base-topic (default luminus/<region>),
--mqtt-discovery-prefix (default homeassistant).
--signing-month YYYY-MM fetches Luminus's public price-list archive
for the month a Comfy contract was signed, instead of the current price
list. This uses the same public, unauthenticated endpoints that power
https://www.luminus.be/nl/apps/prijslijsten-archief/ (look up the
product ID for a region+month, then download that PDF); the archive
covers roughly 2023-09 onward.
With --format text or --format json, this prints a full
contract-vs-current comparison, not just the historical numbers,
because consumption and injection behave differently:
- Consumption is fixed by Luminus for a contract's first 12 months, then Luminus "may adjust" it per the general terms — with no public formula for what it becomes. So: if your contract is still within its first 12 months (checked from the signing month, printed in the output), current consumption == the contract price shown. Past that window, there's no public way to derive the current value — check a recent bill.
- Injection (solar feed-in) is different: it's re-indexed monthly by a formula fixed at contract signing and printed on the contract's own signing-month PDF. That formula's coefficients are your contract's, not necessarily whatever Luminus currently quotes new signups — confirmed they can differ between contract vintages. So "current injection" here means your contract's own formula applied to this month's published Belpex index value (also pulled from Luminus's own current PDF, since it's market data that can't live in a static past contract — but only the index number is taken from there, not a pre-computed injection price). Expect the result to differ by ~0.01 c€/kWh from your actual bill, since Luminus prints the Belpex value rounded to 2 decimals.
Still not the same as your current bill for consumption past the
first year, and there is no way to get your live, personal,
currently-indexed price without logging into MyLuminus; that page is
account-gated by design and out of scope for this tool. --format mqtt
with --signing-month publishes the full comparison too (see the MQTT
section above).
This repo is a HACS-installable "Integration" repository: it has a
hacs.json at the root and the component lives at
custom_components/luminus_price/.
- HACS → the three-dot menu → Custom repositories.
- Add this repo's URL, category Integration.
- Install "Luminus Comfy Price", restart Home Assistant.
- Settings → Devices & services → Add integration → search
"Luminus Comfy Price" → pick a region, and optionally your
contract's signing month (
YYYY-MM) for the contract-vs-current comparison.
Repeat step 4 once per region you want tracked — each region is its own config entry (and its own device), matching the CLI script's "run once per region" model. The signing month can be added, changed, or cleared later without removing the integration: open the integration's entry → Configure.
Per configured region, one device ("Luminus Comfy Electricity (<Region>)") with:
- Always: Consumption Peak, Consumption Off-Peak, Injection Peak,
Injection Off-Peak, Fixed Fee — the current price. If a signing month
is configured, Consumption Peak/Off-Peak track the contract's fixed
price while still within its first 12 months (
unknownafterward — see--signing-monthabove for why), and Injection Peak/Off-Peak are computed from your contract's own formula, not just read off the current price list. - Only with a signing month configured (disabled by default in the
entity registry — enable from the device's entity list if you want
them): Contract Consumption Peak/Off-Peak, Contract Injection
Peak/Off-Peak — your contract's original signing-month prices, for
reference/comparison. Report
unknownif no signing month is set.
Polling: every 4 hours normally. If a fetch fails (entity goes unavailable), it retries every 10 minutes until one succeeds, then falls back to the 4-hour interval.
Published to evenssolutions/luminus-pricing.
HACS tracks versions via git tags — manifest.json's "version" is
bumped and a matching tag (e.g. v0.0.1) is created for each release.
pip install -r requirements-dev.txt
pytesttests/test_api.py parses frozen PDF snapshots
(tests/fixtures/comfy_flanders_202608.pdf, a current-price PDF, and
comfy_flanders_202401_signing.pdf, an archived signing-month PDF with
a different table label for the injection row and different
injection-formula coefficients) rather than hitting the network, so it
catches regressions in both the price-table parser and the injection
formula parser, independent of Luminus's current prices or endpoint
availability. tests/test_signing_comparison.py covers the "is
consumption still fixed?" date-window logic used by --signing-month.
- Breaking change if you installed the integration before this
version:
DoubleTariffPrice'speak_ckwh/off_peak_ckwhfields (and matching CLI JSON keys / MQTT sensor keys) are nowconsumption_peak_ckwh/consumption_off_peak_ckwh, for consistency with the newcontract_consumption_*/current_consumption_*fields. In Home Assistant this changes entity unique IDs, so you'll get newsensor.consumption_peakentities and should remove the oldsensor.peak/sensor.off_peakones manually (any history on the old entities won't carry over). - Prices are Luminus's currently published, monthly-indexed rates — re-fetch (script re-run, or the HA integration's next poll) for current figures.
- The Brussels region requires an extra
isBrussels=truequery parameter alongsidetabValue=Brussels, or the endpoint 404s; this is handled internally. - The CLI script and the HA integration share one fetch/parse
implementation (
custom_components/luminus_price/api.py) — the CLI imports it rather than duplicating it, soluminus_price.pyonly works when run from inside this repo checkout (it needscustom_components/luminus_price/alongside it).