# HAMeter Read utility meters (electric, gas, water) via SDR and publish to Home Assistant over MQTT. HAMeter uses an RTL-SDR dongle to receive wireless transmissions from ERT-equipped utility meters, decodes them with [rtlamr](https://github.com/bemasher/rtlamr), and publishes readings to Home Assistant via MQTT auto-discovery. ## Features - **Web UI** for all configuration — setup wizard, meter management, calibration, discovery, live logs - Reads SCM, SCM+, IDM, NetIDM, R900, and R900BCD meter protocols - Home Assistant MQTT auto-discovery — sensors appear automatically - Four HA sensors per meter: calibrated reading, raw reading, last seen, cost (if enabled) - Built-in calibration tool to match raw readings to your physical meter display - Discovery mode to scan for nearby meter IDs - Cost tracking with per-unit rates and fixed charges - Real-time dashboard with Server-Sent Events - Configuration export/import (JSON) - Persistent config and cost state across restarts ## Requirements - RTL-SDR dongle (tested with Nooelec RTL-SDR v5) - 915 MHz antenna (or telescopic whip at ~8 cm for 900 MHz band) - Docker host with USB access (tested on Unraid) - MQTT broker (e.g., Mosquitto) - Home Assistant with MQTT integration ## Quick Start ### 1. Install Mosquitto MQTT Broker Install via your Docker host's app store or: ```bash docker run -d --name mosquitto -p 1883:1883 eclipse-mosquitto ``` ### 2. Add MQTT Integration to Home Assistant **Settings > Devices & Services > Add Integration > MQTT** - Broker: your Docker host IP - Port: 1883 ### 3. Connect the SDR Dongle Plug the RTL-SDR into a **USB 2.0 port** (USB 3.0 can cause interference). Blacklist the kernel DVB driver so it doesn't claim the device: ```bash # On Unraid (persists across reboots): mkdir -p /boot/config/modprobe.d echo "blacklist dvb_usb_rtl28xxu" >> /boot/config/modprobe.d/rtlsdr.conf rmmod dvb_usb_rtl28xxu 2>/dev/null # On other Linux: echo "blacklist dvb_usb_rtl28xxu" | sudo tee /etc/modprobe.d/rtlsdr.conf sudo rmmod dvb_usb_rtl28xxu 2>/dev/null ``` ### 4. Run HAMeter ```bash docker run -d \ --name hameter \ --restart unless-stopped \ --device=/dev/bus/usb \ -p 9090:9090 \ -v /mnt/user/appdata/hameter:/data \ hameter:latest ``` Open `http://:9090` in a browser. The setup wizard will guide you through MQTT configuration and adding your first meter. ## Docker Configuration | Parameter | Required | Description | |-----------|----------|-------------| | `-p 9090:9090` | Yes | Web UI port | | `-v /path/to/data:/data` | Yes | Persistent storage for config and cost state | | `--device=/dev/bus/usb` | Yes | USB passthrough for the RTL-SDR dongle | | `-e FLASK_SECRET_KEY=...` | No | Flask session secret (random value generated if not set) | All meter, MQTT, and general settings are configured through the web UI — no environment variables needed. ## Web UI After starting the container, open `http://:9090` to access: | Page | Description | |------|-------------| | **Dashboard** | Live meter readings, costs, pipeline status | | **Discovery** | Scan for nearby meter transmissions | | **Meters** | Add, edit, delete meters; manage cost rates | | **Calibration** | Calculate and apply calibration multipliers | | **MQTT Settings** | Configure broker connection; test connectivity | | **General Settings** | SDR device ID, RTL-TCP settings, log level | | **Logs** | Real-time application log viewer | | **Config Export/Import** | Backup and restore configuration | ## Finding Your Meter ID Your meter has two numbers: - **Billing number** — printed on the faceplate (e.g., `2698881`). **Don't use this.** - **Radio serial number (ERT ID)** — transmitted over RF (e.g., `23040293`). **Use this one.** To find the ERT ID, go to the **Discovery** page in the web UI and click **Start Discovery**. HAMeter will listen for all nearby meter transmissions and display them in a table sorted by frequency. Your meter will likely be the one with the highest count (since it's closest). You can add a discovered meter directly from the results. ## Calibration The raw value from the meter's radio does not directly equal the reading on the meter display. You need a calibration multiplier to convert raw values to actual units (kWh, gallons, etc.). ### How to Calibrate 1. Go to the **Calibration** page in the web UI 2. Select your meter from the dropdown 3. Enter the **raw reading** shown on the dashboard 4. Read the display on your **physical meter** and enter that value 5. Click **Calculate** — the multiplier is computed automatically 6. Review the preview, then click **Apply Multiplier** The pipeline will restart with the new multiplier, and cached readings are cleared so the dashboard shows clean data. ### Why Is Calibration Needed? ERT meters transmit a raw register value, not the displayed reading. The relationship between the two depends on the meter's internal configuration (Kh factor, register multiplier, number of dials). The multiplier accounts for all of these. Common multipliers are `0.01` (raw is in Wh, display is kWh) or `0.1`. ## Cost Tracking HAMeter can track estimated utility costs based on your rate structure. ### Setup 1. Go to **Meters** and edit a meter 2. Under **Rate Components (Cost Factors)**, add your rate entries: - **Per Unit** — variable rate charged per unit consumed (e.g., $0.12/kWh) - **Fixed** — flat monthly charges (e.g., $15 service fee) 3. Save the meter ### How It Works - **Per-unit costs** are calculated automatically with each meter reading - **Fixed charges** are applied manually from the dashboard when your billing cycle starts - **Billing period reset** sets the cumulative cost back to $0 for a new cycle - Cost state is persisted to disk and survives container restarts ### Home Assistant When cost tracking is enabled for a meter, an additional **Cost** sensor is published via MQTT auto-discovery (e.g., `sensor.electric_meter_cost`) with `device_class: monetary` and `state_class: total`. ## Home Assistant Sensors Each configured meter creates sensors in HA via MQTT auto-discovery: | Sensor | Example Entity | Description | |--------|---------------|-------------| | **Reading** | `sensor.electric_meter_reading` | Calibrated value (e.g., `5,316 kWh`) | | **Raw Reading** | `sensor.electric_meter_raw_reading` | Unconverted register value (diagnostic) | | **Last Seen** | `sensor.electric_meter_last_seen` | Timestamp of last received transmission | | **Cost** | `sensor.electric_meter_cost` | Cumulative cost (only if cost_factors configured) | All sensors are grouped under a single device in HA with the manufacturer shown as "HAMeter". ### Energy Dashboard To add your electric meter to the HA Energy Dashboard: 1. Go to **Settings > Dashboards > Energy** 2. Under **Electricity Grid**, click **Add Consumption** 3. Select `sensor.electric_meter_reading` 4. Optionally use the cost sensor or set a static cost per kWh ## Smart Defaults When you set a device class for a meter, the unit and icon are set automatically: | Device Class | Icon | Unit | |-------------|------|------| | `energy` | `mdi:flash` | `kWh` | | `gas` | `mdi:fire` | `ft³` | | `water` | `mdi:water` | `gal` | You can override these in the meter edit form. ## Configuration File Configuration is stored at `/data/config.json` inside the container. You can back it up via the **Config Export** feature in the web UI, or by copying the file directly from the mounted volume. Cost state is stored separately at `/data/cost_state.json`. ### YAML Migration If you have a legacy `hameter.yaml` config file at `/config/hameter.yaml` or `/app/config/hameter.yaml`, HAMeter will automatically migrate it to JSON on first startup. ## Unraid Setup ### Via Docker UI 1. Go to **Docker > Add Container** 2. Set **Repository** to `hameter:latest` (or your registry path) 3. Set **Network Type** to Host (or bridge with port 9090 mapped) 4. Add **Device**: `/dev/bus/usb` 5. Add **Path**: Container `/data` mapped to `/mnt/user/appdata/hameter` 6. Start the container and open the web UI at `http://:9090` ### USB Passthrough The SDR dongle must be accessible inside the container. Use `--device=/dev/bus/usb` to pass the entire USB bus (simplest and most reliable across reboots). ## Building from Source ```bash git clone https://github.com/your-username/HAMeter.git cd HAMeter docker build -t hameter:latest . ``` The Dockerfile is a multi-stage build: 1. **Go** — compiles `rtlamr` from source 2. **C** — compiles `rtl-sdr` (librtlsdr + rtl_tcp) from source 3. **Python** — runtime with Flask, paho-mqtt, and pyyaml ## API HAMeter exposes a REST API at the same port as the web UI. Key endpoints: | Endpoint | Method | Description | |----------|--------|-------------| | `/api/status` | GET | Pipeline status | | `/api/readings` | GET | Current meter readings | | `/api/costs` | GET | Cost state for all meters | | `/api/events` | GET | SSE stream for live updates | | `/api/setup` | POST | Initial setup | | `/api/config/mqtt` | GET/POST | MQTT configuration | | `/api/config/mqtt/test` | POST | Test MQTT connection | | `/api/config/meters` | GET/POST | List/add meters | | `/api/config/meters/` | PUT/DELETE | Update/delete a meter | | `/api/config/general` | GET/POST | General settings | | `/api/config/export` | GET | Export configuration | | `/api/config/import` | POST | Import configuration | | `/api/discovery/start` | POST | Start meter discovery | | `/api/discovery/stop` | POST | Stop discovery | | `/api/discovery/results` | GET | Get discovery results | | `/api/discovery/add/` | POST | Add discovered meter | | `/api/calibration/calculate` | POST | Calculate multiplier | | `/api/calibration/apply` | POST | Apply multiplier | | `/api/readings/clear` | POST | Clear cached readings | | `/api/costs//reset` | POST | Reset billing period | | `/api/costs//add-fixed` | POST | Apply fixed charges | | `/api/pipeline/restart` | POST | Restart pipeline | ## Supported Meters HAMeter supports any ERT meter that transmits on the 900 MHz ISM band: | Protocol | Typical Manufacturer | Meter Type | |----------|---------------------|------------| | SCM | Itron | Electric, Gas | | SCM+ | Itron | Electric, Gas | | IDM | Itron | Electric (with interval data) | | NetIDM | Itron | Electric (net metering) | | R900 | Neptune | Water | | R900BCD | Neptune | Water | ## Troubleshooting ### No readings appearing - Verify the SDR dongle is on a **USB 2.0 port** (USB 3.0 causes interference) - Check that the antenna is attached - Run Discovery from the web UI to confirm the SDR is receiving signals - Check the Logs page or `docker logs hameter` ### "usb_open error -3" The kernel DVB driver is claiming the device: ```bash rmmod dvb_usb_rtl28xxu ``` ### MQTT connection refused - Verify Mosquitto is running: `docker ps | grep mosquitto` - Use the **Test Connection** button on the MQTT settings page - If using Docker bridge networking, use the host IP (not `127.0.0.1`) ### Sensors not appearing in HA - Verify the MQTT integration is configured in HA - In HA, go to **Developer Tools > MQTT > Listen** and subscribe to `hameter/#` to check if messages are arriving - Restart HA if sensors don't appear after the first discovery publish ### Pipeline stuck or not starting - Check the Logs page for error details - Use **Restart Pipeline** from the dashboard - Verify the SDR dongle is connected and not in use by another application ## License MIT