Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Home Utilities Consumption Meter

Reads household utility meters over radio and shows what the house actually used, by hour and by day, on a private web page the whole family can open.

Water works today. Electricity is written and waiting on my distribution operator to switch the meter's radio on. Gas needs a pulse sensor I have not fitted yet.

Live demo: https://home-utilities-demo.pawels-apps.workers.dev, password demo. Made-up household, a new reading every hour, and the headcount buttons work, so go ahead and change them.

What it looks like

The panel on a laptop, in Polish

Today's consumption, the headcount for the day, and the cards that compare this week and this month against the same stretch of the one before.

The thirty day chart in English, dark theme

Weekends are tinted, public holidays get their own colour, and days the meter missed are drawn as estimates rather than passed off as measurements. Polish and English, light and dark, all switched from the header.

The day view on a phone, with the calendar open

Any past day, hour by hour. The calendar follows the language chosen in the panel rather than the one the browser happens to be set to.

Screenshots are from the demo data, not a real household.

What it does

A meter counts upward forever. It never tells you what a Tuesday cost. This project reads the counter every hour and does the subtraction, so you get:

  • consumption per hour for any day you pick, with hours nobody measured left blank rather than guessed
  • this week against the same days of last week, and this month against the same days of last month
  • litres or kWh per person, using a household size you set in the panel, because ours swings between two and six people depending on who is visiting
  • Polish public holidays marked on the charts, including the Easter-based ones, and a card comparing holidays and weekends against working days
  • Polish and English, switched from the header, with dates, decimal separators and plurals following whichever one is picked
  • one Telegram message a morning with the last 24 hours, and an alert the hour a reading fails because the dongle vanished or the meter went quiet

Everything sits behind one shared password. There are no accounts to manage, which matters when the people using it are my family.

How it is put together

Two halves that talk over one HTTP endpoint.

The agent runs on a Linux machine near the meters. A systemd timer wakes it hourly, it listens for a single wireless M-Bus frame, writes it to a local file and posts it to the panel. If the panel is unreachable the reading is still on disk. A second timer sends the daily Telegram summary, built from those files rather than the radio, so it works even when the dongle is busy.

The panel is a Cloudflare Worker with a D1 database. It stores raw readings, derives the per-day and per-hour tables from them, and serves the dashboard. On the free plan this costs nothing: one reading an hour is 24 rows a day.

Readings are never edited in place. The day and hour tables are rebuilt from the raw readings whenever something changes, so correcting the household size for last Tuesday recalculates that day's per-person figures immediately.

Which meters, which utilities

Built in Poland against real meters, so the notes name real companies:

  • Water. A Diehl IZAR radio module, the kind the local wodociągi fit as standard when they swap a meter. Mine came from the water utility in Starachowice. It broadcasts the counter in clear text, so there is nothing to request from anyone and no key to wait for. This is the one that works today.
  • Electricity. An Apator Otus 3 with a wireless M-Bus HAN interface, the meter PGE Dystrybucja fits here in Świętokrzyskie. The radio arrives switched off and the frames are encrypted, so the operator has to enable the interface and give you the AES-128 key. PGE Dystrybucja accepts a written request and has two months to answer, free of charge. Tauron, Enea and Energa carry the same obligation under the same law.
  • Gas. A Metrix (Apator) G4 with no radio at all, which is what tariff W-2 households usually have. It needs a pulse transmitter on the index dial instead, so it is the one still waiting on parts.

Nothing here is specific to those two companies. Any meter that speaks wireless M-Bus on 868 MHz works the same way, and anything else can post readings to the same HTTP endpoint.

Hardware

Short version: a Linux box that stays on and a 15 EUR RTL-SDR dongle covers water and electricity. Gas needs a pulse transmitter and an ESP board instead.

Full list, including which dongles to avoid and why: docs/hardware.md.

Setup

1. The panel

cd panel
npm install
cp wrangler.jsonc.example wrangler.jsonc

npx wrangler d1 create utilities-db          # paste the id into wrangler.jsonc
npx wrangler d1 execute utilities-db --remote --file=schema.sql

node scripts/hash-password.mjs 'household password' | npx wrangler secret put PASSWORD_HASH
openssl rand -base64 32 | npx wrangler secret put SESSION_SECRET
openssl rand -hex 24    | npx wrangler secret put INGEST_TOKEN   # keep this, the agent needs it

npm run deploy

Set UTILITIES in wrangler.jsonc to the ones you actually have, for example "water,electricity". The switcher at the top of the page hides itself when there is only one.

To see the dashboard with data before any meter is wired up, put the same three secrets in a local .dev.vars and seed it:

printf 'PASSWORD_HASH=%s\nSESSION_SECRET=dev\nINGEST_TOKEN=dev-token\n' \
  "$(node scripts/hash-password.mjs 'demo')" > .dev.vars
npx wrangler d1 execute utilities-db --local --file=schema.sql
npm run dev
node scripts/seed-demo.mjs http://localhost:8799 dev-token water 60

Delete the samples afterwards with DELETE /api/readings?test=1&u=water.

2. The agent

On the Linux machine, as root:

git clone https://github.com/pawisoon/HomeUtilitiesConsumptionMeter.git
cd HomeUtilitiesConsumptionMeter/agent
sudo ./install.sh --deps        # installs rtl-sdr, builds rtl_wmbus and wmbusmeters

Then fill in /etc/home-utilities/config.env with the panel address and the ingest token, and create one meter file per meter. To find out what your meter calls itself, listen for a couple of minutes and read the output:

wmbusmeters --listento=t1,c1,s1 rtlwmbus

Every meter within radio range shows up, so expect the neighbours. Match the id against the serial printed on your own meter. Copy the driver name and id into /etc/home-utilities/meters.d/water.conf, then:

/opt/home-utilities/bin/read-meter.sh water
systemctl enable --now home-utilities-read@water.timer
systemctl enable --now home-utilities-report.timer

With one dongle and more than one meter, give each meter its own minute so they do not fight over the radio. The timer template fires at three minutes past the hour, so move the second one:

mkdir -p /etc/systemd/system/home-utilities-read@electricity.timer.d
printf '[Timer]\nOnCalendar=\nOnCalendar=*-*-* *:33:00 UTC\n' \
  > /etc/systemd/system/home-utilities-read@electricity.timer.d/override.conf
systemctl daemon-reload

The empty OnCalendar= clears the inherited schedule before setting the new one.

Per-meter notes: water, electricity, gas. To run a public demo of your own, see docs/demo.md.

Adding a utility later

The database, the API and the dashboard all take a utility name, so adding one is configuration rather than code:

  1. Add it to UTILITIES in wrangler.jsonc and redeploy.
  2. Drop a new file in /etc/home-utilities/meters.d/ and enable its timer.

water, electricity and gas are defined in panel/src/utilities.ts with their units and rounding. Anything else means adding an entry there.

Upgrading from a water-only install

Databases created before the utility column exists need one migration, which labels the existing rows as water:

npx wrangler d1 execute utilities-db --remote --file=migrations/0001-single-to-multi-utility.sql

If you ran that migration before 2026-09-18, run the repair as well. The first version of it copied per-person figures that were held in litres into a column that holds cubic metres, so the holidays card showed numbers a thousand times too large:

npx wrangler d1 execute utilities-db --remote --file=migrations/0002-fix-per-person-units.sql

Waiting also works. Every day row is derived from the raw readings, and the nightly job rebuilds the last 45 days.

Repository layout

agent/     reader for the Linux machine: scripts, systemd units, install.sh
panel/     Cloudflare Worker, D1 schema, dashboard
docs/      hardware and per-utility setup notes

Limitations

The holiday calendar is Polish only, though panel/src/holidays.ts is a single self-contained file if you want to swap in another country's.

The agent reads wireless M-Bus meters. Anything else needs its own reader that posts to the same endpoint, which is one HTTP call:

POST /api/ingest
Authorization: Bearer <INGEST_TOKEN>

{"utility": "water", "timestamp": "2026-09-10T06:03:00Z", "total": 12.681}

The rest of the endpoints are in docs/api.md.

Hourly readings on a shared dongle interrupt whatever else uses it, briefly, once an hour. With a dedicated dongle there is no interruption.

Licence

MIT. See LICENSE.

About

Read household water, electricity and gas meters over wireless M-Bus (868 MHz) with an RTL-SDR dongle, and see hourly and daily usage on a private Cloudflare Workers panel. Works with Diehl IZAR water meters fitted by Polish wodociagi and Apator Otus 3 electricity meters from PGE Dystrybucja. Polish and English.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages