diff --git a/README.md b/README.md index 8454388..81744a8 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,16 @@ HAMeter uses an RTL-SDR dongle to receive wireless transmissions from ERT-equipp ## Features -- Reads SCM, SCM+, IDM, and R900 (Neptune) meter protocols -- Built-in calibration multiplier per meter +- **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 -- Three HA sensors per meter: calibrated reading, raw reading, last seen -- Discovery mode to find nearby meter IDs -- Fully configurable via Docker environment variables — no config files needed -- Smart defaults based on meter type (energy, gas, water) -- Supports up to 9 meters simultaneously +- 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 @@ -35,7 +37,7 @@ docker run -d --name mosquitto -p 1883:1883 eclipse-mosquitto ### 2. Add MQTT Integration to Home Assistant -**Settings → Devices & Services → Add Integration → MQTT** +**Settings > Devices & Services > Add Integration > MQTT** - Broker: your Docker host IP - Port: 1883 @@ -64,74 +66,38 @@ docker run -d \ --name hameter \ --restart unless-stopped \ --device=/dev/bus/usb \ - -e MQTT_HOST=192.168.1.74 \ - -e METER_1_ID=23040293 \ - -e METER_1_PROTOCOL=scm \ - -e METER_1_NAME="Electric Meter" \ - -e METER_1_DEVICE_CLASS=energy \ - -e METER_1_MULTIPLIER=1.0 \ + -p 9090:9090 \ + -v /mnt/user/appdata/hameter:/data \ hameter:latest ``` -Your meter should appear in Home Assistant within a minute. +Open `http://:9090` in a browser. The setup wizard will guide you through MQTT configuration and adding your first meter. -## Configuration +## Docker Configuration -All configuration is done via environment variables. No config file needed. +| 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) | -### Required Variables +All meter, MQTT, and general settings are configured through the web UI — no environment variables needed. -| Variable | Description | -|----------|-------------| -| `MQTT_HOST` | MQTT broker IP address | -| `METER_1_ID` | Meter radio serial number (ERT ID, not the billing number) | -| `METER_1_PROTOCOL` | Protocol: `scm`, `scm+`, `idm`, `r900`, `r900bcd`, `netidm` | +## Web UI -### Optional Variables +After starting the container, open `http://:9090` to access: -| Variable | Default | Description | -|----------|---------|-------------| -| `MQTT_PORT` | `1883` | MQTT broker port | -| `MQTT_USER` | *(empty)* | MQTT username | -| `MQTT_PASSWORD` | *(empty)* | MQTT password | -| `MQTT_BASE_TOPIC` | `hameter` | Base MQTT topic | -| `METER_1_NAME` | `Electric Meter` | Friendly name in HA | -| `METER_1_MULTIPLIER` | `1.0` | Calibration multiplier (see below) | -| `METER_1_DEVICE_CLASS` | `energy` | HA device class — sets smart defaults | -| `METER_1_UNIT` | *(auto)* | Unit of measurement | -| `METER_1_ICON` | *(auto)* | HA icon | -| `METER_1_STATE_CLASS` | `total_increasing` | HA state class | -| `SDR_DEVICE_ID` | `0` | RTL-SDR device index | -| `LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARNING`, `ERROR` | - -### Smart Defaults by Device Class - -When you set `METER_1_DEVICE_CLASS`, the icon and unit are set automatically: - -| Device Class | Icon | Unit | -|-------------|------|------| -| `energy` | `mdi:flash` | `kWh` | -| `gas` | `mdi:fire` | `ft³` | -| `water` | `mdi:water` | `gal` | - -You can override any of these with the explicit `METER_1_ICON` or `METER_1_UNIT` variables. - -### Multiple Meters - -Add meters using `METER_2_*`, `METER_3_*`, etc. (up to `METER_9_*`): - -```bash --e METER_1_ID=23040293 \ --e METER_1_PROTOCOL=scm \ --e METER_1_NAME="Electric Meter" \ --e METER_1_DEVICE_CLASS=energy \ --e METER_2_ID=55512345 \ --e METER_2_PROTOCOL=r900 \ --e METER_2_NAME="Water Meter" \ --e METER_2_DEVICE_CLASS=water \ -``` - -Gaps are fine (e.g., meter 1 and 3 without 2). +| 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 @@ -139,30 +105,7 @@ 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, run HAMeter in discovery mode: - -```bash -docker run --rm \ - --device=/dev/bus/usb \ - -e MQTT_HOST=localhost \ - -e METER_1_ID=0 \ - -e METER_1_PROTOCOL=scm \ - hameter:latest --discover --discover-duration 120 -``` - -This listens for all nearby meter transmissions for 2 minutes and prints a summary: - -``` -DISCOVERY SUMMARY — 12 unique meters found -============================================================ -Meter ID Protocol Count Last Reading --------------------------------------------------- -23040293 SCM 8 519161 -55512345 R900 3 12345678 -... -``` - -Cross-reference the IDs with your physical meters to identify which is yours. Your meter will likely be the one with the highest count (since it's closest). +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 @@ -170,60 +113,100 @@ The raw value from the meter's radio does not directly equal the reading on the ### How to Calibrate -**Step 1: Read your physical meter** +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** -Go to the meter and write down the display reading. For example: `59,669 kWh` - -**Step 2: Get the raw reading from HA** - -In Home Assistant, go to **Developer Tools → States** and find your meter's raw reading sensor. For example: `sensor.electric_meter_raw_reading` shows `516,030` - -Take both readings at roughly the same time so they correspond. - -**Step 3: Calculate the multiplier** - -Divide the physical reading by the raw reading: - -``` -multiplier = physical_reading / raw_reading -multiplier = 59669 / 516030 -multiplier = 0.1156 -``` - -**Step 4: Set the multiplier** - -Set `METER_1_MULTIPLIER=0.1156` in your Docker environment variables and restart the container. - -### Verifying Calibration - -After setting the multiplier, the **Reading** sensor in HA should closely match your physical meter display. If it drifts over time, repeat the process to recalculate. - -The **Raw Reading** sensor (shown as a diagnostic entity in HA) always shows the unconverted value, so you can recalibrate at any time without removing the multiplier first. +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. +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 three sensors in HA: +Each configured meter creates sensors in HA via MQTT auto-discovery: | Sensor | Example Entity | Description | |--------|---------------|-------------| -| **Reading** | `sensor.electric_meter_reading` | Calibrated value (e.g., `59,669 kWh`) | +| **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 three are grouped under a single device in HA with the manufacturer shown as "HAMeter". +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** +1. Go to **Settings > Dashboards > Energy** 2. Under **Electricity Grid**, click **Add Consumption** 3. Select `sensor.electric_meter_reading` -4. Optionally set a cost per kWh +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 @@ -236,46 +219,36 @@ 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 `paho-mqtt` and `pyyaml` +3. **Python** — runtime with Flask, paho-mqtt, and pyyaml -## Unraid Setup +## API -### Via Docker UI +HAMeter exposes a REST API at the same port as the web UI. Key endpoints: -1. Go to **Docker → Add Container** -2. Set **Repository** to `hameter:latest` -3. Set **Network** to Host -4. Set **Extra Parameters** to `--device=/dev/bus/usb` -5. Add environment variables for `MQTT_HOST`, `METER_1_ID`, `METER_1_PROTOCOL`, etc. -6. Start the container - -### 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). - -## 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 mode to confirm the SDR is receiving signals -- Check logs: `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` -- Check `MQTT_HOST` and `MQTT_PORT` are correct -- 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 +| 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 @@ -286,9 +259,38 @@ HAMeter supports any ERT meter that transmits on the 900 MHz ISM band: | 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 | -| NetIDM | Itron | Electric (net metering) | + +## 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