👉👉 For a quick how-to, visit my blog article about gonrg 👈👈
CLI tool, server, and Go library to connect to the optical D0 interfaces of power meters and gather OBIS values (e.g. power consumption).
For this to work, you need a gadget to connect to the optical D0 interface of your meter, which you preferably connect to your host via USB, but any (tunneled) serial connection should also be fine.
Text-based D0 OBIS is currently supported (which most digital meters in Germany use).
For newer meters, SML mode is supported (--sml).
The binary can be used as a standalone CLI tool, as a JSON/REST server serving OBIS meter data, or as a client to connect with a remote gonrg server.
The following meters have been verified to be directly compatible:
- eBZ DD3, many variants
- Power meter, consumption and production (two-direction).
- Tested with and without extended data (detailed per-phase power).
- Landis+Gyr Ultraheat T550
- District heating meter
- Required params:
--baudrate 300 --baudrate-read 2400 --device-option 0preamble --response-delay 300ms - Only poll daily or hourly because the device runs on battery.
- Iskra MT631
- Uses SML
- Required params:
--sml
SML parsing has furthermore been verified with data from various devices. I was able to implement it thanks to this awesome deep-dive from Matthias Röckl (in German).
gonrg is probably compatible with many other devices. If you can verify compatibility, please open a pull request that extends the list.
- Server OpenAPI specification and Swagger UI
- CLI Watch mode
- Client code
- HAN interface support (I don't have any devices available to test with)
Building requires go 1.26.
Just hit make to obtain a statically linked binary in the project root.
john@doe:~/foo$ gonrg --help
⚡️ gonrg - a simple D0 OBIS/SML energy meter CLI tool or server.
Usage:
gonrg [flags]
gonrg [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
server run in server mode given a config
Flags:
-b, --baudrate int baud rate, 0 means choose best option
-r, --baudrate-read int (non-SML) baud rate for reading, 0 means same like baudrate
-t, --d0-timeout duration read timeout of the d0 serial connection
-D, --debug set debug log level
-d, --device string device to read from (default "/dev/ttyUSB0")
-o, --device-option strings device option
-h, --help help for gonrg
-j, --json output json instead of pretty table
-l, --response-delay duration (non-SML) wait before expecting response
-s, --sml connect to an SML device rather than a plain OBIS device
-S, --strict strict mode for parsing - fail fast
-v, --version version for gonrg
Use "gonrg [command] --help" for more information about a command.
Create a pretty tabular (but not machine-readable) overview over the OBIS data of your meter:
john@doe:~/foo$ gonrg --device /dev/ttyUSB1
⚡️gonrg version 0.1.1
Device ID: EBZ5DD12345ETA_104
Exact Key Simple Key Name Value Unit
1-0:0.0.0*255 0.0.0 1EBZ0102123456
1-0:96.1.0*255 96.1.0 1EBZ0102123456
1-0:1.8.0*255 1.8.0 energy_cons 1924 kWh
1-0:96.5.0*255 96.5.0 001C0104
0-0:96.8.0*255 96.8.0 0086C9BB
The same info can be obtained in JSON with the --json flag appended:
john@doe:~/foo$ gonrg --device /dev/ttyUSB1 --json
{
"measurementTime": "2025-12-15T21:10:29.730101646+01:00",
"deviceID": "EBZ5DD12345ETA_104",
"list": [
{
"exactKey": "1-0:0.0.0*255",
"simplifiedKey": "0.0.0",
"name": "",
"valueText": "1EBZ0102123456",
"valueNum": 0,
"valueScale": 0,
"valueFloat": 0,
"unit": ""
},
{
"exactKey": "1-0:96.1.0*255",
"simplifiedKey": "96.1.0",
"name": "",
"valueText": "1EBZ0102123456",
"valueNum": 0,
"valueScale": 0,
"valueFloat": 0,
"unit": ""
},
{
"exactKey": "1-0:1.8.0*255",
"simplifiedKey": "1.8.0",
"name": "energy_cons",
"valueText": "",
"valueNum": 1924,
"valueScale": 0,
"valueFloat": 1924,
"unit": "kWh"
},
{
"exactKey": "1-0:96.5.0*255",
"simplifiedKey": "96.5.0",
"name": "",
"valueText": "001C0104",
"valueNum": 0,
"valueScale": 0,
"valueFloat": 0,
"unit": ""
},
{
"exactKey": "0-0:96.8.0*255",
"simplifiedKey": "96.8.0",
"name": "",
"valueText": "0086C9BB",
"valueNum": 0,
"valueScale": 0,
"valueFloat": 0,
"unit": ""
}
]
}
You can start a thread-safe server which listens to incoming JSON/REST connections and serves OBIS data.
First, create a server configuration by using the example_server.yaml as a template. Then start the server with:
john@doe:~/foo$ gonrg server -C my_server_config.yaml
You can use curl to test fetching OBIS data:
john@doe:~/foo$ curl http://server:8080/meter/power/1.8.0
{
"measurementTime": "2025-12-15T21:17:57.759573978+01:00",
"deviceID": "EBZ5DD12345ETA_104",
"list": {
"exactKey": "1-0:1.8.0*255",
"simplifiedKey": "1.8.0",
"name": "energy_cons",
"valueText": "",
"valueNum": 234172889972,
"valueScale": 8,
"valueFloat": 2341.72889972,
"unit": "kWh"
}
}
There is a web app demo available. It demonstrates the usage of the following API elements:
- GET
/metersto get a list of the meters, - GET
/meter/<meter>to get the readings data from a meter, - Websocket
/ws/meter/<meter>to keep the readings table updated.
Documentation of the functions and types will be improved very soon!
package main
import (
"fmt"
"time"
"github.com/lnobach/gonrg/d0"
"github.com/lnobach/gonrg/obis"
)
func main() {
// Create a new D0 device to read from.
// If you have a SML device, just use
// sml.NewDevice(d0.DeviceConfig{..})
d, err := d0.NewDevice(d0.DeviceConfig{
Device: "/dev/ttyUSB0",
BaudRate: 9600,
})
if err != nil {
panic(err)
}
// Read the raw data from the device
rawdata, err := d.Get()
if err != nil {
panic(err)
}
// Parse the data
obisdata, err := d0.ParseOBISList(
&d0.ParseConfig{},
rawdata,
time.Now(),
)
// Create a map for faster key-based access
obismap := obis.ListToMap(obisdata)
e, exists := obismap.Map["1.8.0"] // power consumption
if !exists {
panic("no power consumption in data")
}
// Output the current power consumption to stdout
fmt.Printf("Power consumption: %s\n", e.PrettyValue(true))
}- If you encounter errors, try to append the
--debugflag to see what is going on. - Running
make gonrg-mockrenders a binarygonrg-mock, which does not connect to actual meters, but mocks some popular meters which can be used to evaluate gonrg without real hardware. If you specify a non-existing device name, it will give you a list with existing ones in the log.