Wireless M-Bus

Receive data from Wireless Meter-Bus (wM-Bus) devices like gas or electricity meters

Current Release
0.14.1
Developer
Christian Landvogt
License
GPL-2.0-only

This adapter allows to receive wireless M-Bus data from supported receivers. The extent of device implementation varies, but wMBus modes can be configured for all listed devices.

  • Embit WMB modules
  • Amber Wireless AMB8465 (Beware: Command mode (UART_CMD_Out_Enable) is enabled!)
  • IMST iM871A
  • IMST iU891A-XL
  • CUL

The WMBUS stack has been "re-ported" from FHEM project and was extensively fixed and refactored. Testing was done with raw data picked up on the internet, OMS sample data and some test data from the jmbus library. Some edge cases are still untested.

The device creation, updating, etc is mostly based of Apollon77's M-Bus adapter (see below).

If the adapter receives encrypted telegrams the AES key configuration tab should list the device ID automatically.

If the parser fails the raw telegram data will be saved to the info.rawdata state.

Attention: The Amber receiver seems to crash after some time (or amount of received messages) in C mode? Hardware flaw?

IMST iM871A variant: There exists a "RWE Smart Home" USB receiver which is in principle a IMST iM871A, but the kernel will not automatically load the corresponding driver. This is a one-liner to create a udev rule to fix that:

sudo bash -c "echo \
    

ACTION==\"add\", ATTRS{idVendor}==\"10c4\", ATTRS{idProduct}==\"87ed\", RUN+=\"/sbin/modprobe cp210x\" RUN+=\"/bin/sh -c \\'echo 10c4 87ed > /sys/bus/usb-serial/drivers/cp210x/new_id\\'\"' > /etc/udev/rules.d/99-imst.rules"

Links:

Initial setup

The initial setup requires to configure the basics (hardware connection to the wmbus receiver) and to setup AES keys for all encrypted wmbus nodes to be collected. The most tricky part are the AES keys.

Basic setup

This requires to select the appropriate USB device and the correct baud rate (usually for IMST iM871A: 57600 baud; IMST iU891A-XL: 115200 baud; Amber: 9600 baud; Embit: 9600 baud, CUL: 38400 or 9600 baud). Most meters will send in "T Mode".

From version 0.9.0 on, the adapter also supports to connect to serial devices reachable via a TCP socket. However, the admin interface does not really reflect that (for now) and you have to select "custom port" and enter the host as tcp://host:port.

Other options

  • Update unchanged states: When a telegram arrives all states will be updated, even if their value did not change. (default: on)
  • Force energy units to kWh: All energy units (Wh and J) will be converted to kWh. (default: off)
  • Temporarily block device after consecutive failures: If 10 consecutive telegrams of the same device are not parsed successfully the device will be ignored until adapter restart (default: on)
  • Only handle devices that already have an object tree: Telegrams of devices that do not have an object tree yet are ignored, so no new devices are created - useful once every meter you care about has been set up. Telegrams that cannot be decoded at all are ignored just as well: they do not add a device to the AES key list and they are not written to info.rawdata. The automatic block list still counts them, so that an unwanted device stops costing a decoding attempt - it just does not say so in the log. A device you delete from the object tree is gone for good right away. The devices are looked up when the adapter starts, so a device that should be picked up again needs a restart. (default: off)
  • Overwrite name, unit and role of the data states: Name, unit and role of a data state follow the decoded telegram - after an update of the parser, or when "Force energy units to kWh" is switched - but only as long as they are what the adapter wrote itself. A name you gave a state, or a role you changed for another adapter, stays as it is, and so does everything of a state an earlier version of the adapter created. Switch this on to set every data state back to what the adapter would create it with at its next telegram, your own names included, and off again once that has happened. (default: off)

Every data state remembers which data record it was created for - its storage number, tariff, sub-unit, function field (instantaneous, maximum, minimum, error state) and VIF extensions. The id of a state only names the position of a record in the telegram, its storage number and its type, so a meter that sends its records in different orders, or telegrams of different layouts, can put another record at the same id. Such a value is skipped instead of being written to a state that describes a different record, and the log says so once per state. A state created by an earlier version of the adapter takes the first record that arrives after the update as the one it stands for.

Compact telegrams (used by some Kamstrup devices) are supported automatically: the structure of a full telegram is remembered - with the device, so that it survives a restart of the adapter - and reused to decode the compact ones. Only the compact telegrams a device sends before it has sent a full one for the first time cannot be decoded and are silently skipped.

AES keys

The device identifier is a combination of the manufacturer code and the device ID (e.g. AAA-12345678). The key can be entered either as a plain-text key with 16 characters or as a hex string with 32 characters (16 bytes).

The easiest way to setup the keys is to start the adapter without any key setup and to wait for an encrypted telegram, after which an entry with "UNKNOWN" key is generated by the adapter. (With "only handle devices that already have an object tree" switched on, no device can be waiting for a key - the button is disabled then and says so.) Then you can fill in the corresponding key and save the settings. If you see devices you don't know or just want to get rid of (e.g. devices from neighbours), you can enter them in the blocked devices tab (see below).

Blocking unwanted devices

The "blocked devices" tab allows you to complete stop the adapter from handling telegrams from unwanted devices.

You only need to enter the device ID (e.g. AAA-12345678), which you can get from the object tree after a telegram has been received and parsed or from the (debug) log.

Afterwards, when you delete the device from the object tree, the adapter will not recreate it again.

Manufacturer specific data

Some meters put values the standard does not describe into a manufacturer specific data record: a blob of a few bytes with several values packed into it. The parser knows a few of them already (an Itron smoke detector, for instance) and writes their values as states of their own. For every other meter the blob is one state holding a large number - and the "Manufacturer specific data" tab is where you describe what is in it.

A description is JSON, one entry per manufacturer code - the three letters of the device address:

{
    "ACM": [
        { "byte": 0, "bit": 0, "description": "Backflow detected" },
        { "byte": 1, "description": "Battery", "unit": "%", "legacyName": "VIF_BATTERY_PERCENT" },
        { "byte": 2, "bytes": 3, "description": "Volume", "unit": "l" },
        { "byte": 5, "flags": ["Leakage", "Burst", null, "Removal"] },
        { "byte": 6, "bits": [0, 1], "description": "Network mode", "values": ["Off", "Walk-by", "Fixed"] }
    ]
}
keywhat it does
bytewhere the value starts in the blob, counted from zero (required)
byteshow many bytes it is (default 1)
bit / bitsa single bit, or an inclusive range like [0, 1], of that byte
descriptionwhat the value is - it becomes the name of the state (required unless flags is given)
flagsone name per bit of the byte, null for a bit to skip - each name becomes a state of its own
valuesnames for the numbers a field can hold, as a list or as an object like { "4": "Water" }
unitthe unit of the state
legacyNamethe VIF_… part of the state id, see below
storageNo, tariffoverride what the record they came from says

The table above is a summary. Every field a description can hold, with what each one does to the value, is documented with the parser itself: wireless-mbus-parser, "Describing a blob instead of decoding it"

  • the same link sits under the editor in the tab. Insert an example puts a description of a meter that does not exist into the editor, with one of every kind of field in it, which is the quickest way to start: replace its XXX with the manufacturer code of your meter.

If a manufacturer uses more than one kind of blob, describe each one as a layout with the device types and the VIF it belongs to - the first one that matches decodes the blob:

{ "ACM": [{ "deviceType": 7, "fields": [ ... ] }, { "deviceType": [4, 12], "fields": [ ... ] }] }

The descriptions are edited in the tab's JSON editor, which highlights them and marks a syntax error while you type. Two buttons belong to it. Check the descriptions hands them to the parser and reports what it makes of them, with the exact complaint for one it rejects ("bit 9 is outside of the field at byte 0"). Decode it with the descriptions takes a telegram as hex - from the debug log, for instance - and lists every state it would write in a table, so a description can be tried before it is saved. Neither button writes anything.

The telegram and the decoded table are part of the instance configuration, so they are still there when you come back to the tab. The adapter never reads them - they are a scratch pad.

Two things are worth knowing:

  • The state id is derived from the description of its field, so "description": "Battery" becomes …-VIF_BATTERY. Fixing a typo in a description therefore renames the state. legacyName sets that part of the id directly and keeps it stable, which is worth doing for every value you intend to keep.
  • A description replaces the one the parser ships for that manufacturer, rather than adding to it. Describing one value of an Itron smoke detector means its other 25 are no longer written.

Telegram variants

Most meters send telegrams of one layout. Some send a second one now and then - the values of the last billing period, or setup data - and a few alternate between several. The adapter counts every layout of a device as a variant, named by the checksum of its record headers (e.g. 3A7F). The full and the compact telegrams of a layout are the same variant. What it has seen is kept with the device object, so it survives a restart; the counters are written at most once an hour, and a new variant right away.

The "Telegram variants" tab shows them: "Show the telegram variants" lists every variant of every device, with the kind of frames, how many telegrams were seen, when it was seen first and last, and the states its records are written to. None of the variants is treated as the better one - a value of each goes to the state of its own record. If you do not want the values of a variant at all, enter the device address and the variant in the list of ignored telegram variants: its telegrams are dropped once they are decoded, and they are still counted in the table. The device address is the full one, with the manufacturer code (LSE-58511882, not 58511882); the log says so at the start for a row that can never match.

Updating from 0.11.x

Version 0.12.0 replaces the built-in telegram parser with the wireless-mbus-parser library. Object IDs stay as they are, but four things change:

  • Measured values are numbers now, not preformatted text - "474.240" became 474.24. The states have always been of type mixed, so ioBroker itself does not mind, but a history back end that has stored them as text does: InfluxDB refuses numbers for a field that holds strings, and the SQL adapter keeps one type per datapoint, so those series start over. Look at the log of your history adapter after the update and decide per series whether to drop the old data or to keep it beside the new one.
  • Scripts and visualisations that compare or format that text need a look: state.val === '474.240' no longer matches, and a widget that relied on the fixed number of decimals now shows a plain number.
  • Tariff and device unit were read from the wrong bits and are correct now, so the names of the states of a meter with more than one tariff change. Their IDs do not.
  • Reserved and unknown VIFs can be named differently by the library, so a few states of unusual meters appear under a new ID. The old ones stay behind and can be deleted - everything else is written on as before.

The option "Cache for compact frames support" is gone as well: compact telegrams are always supported now, and the first one of a meter no longer counts towards the automatic block list.

Techem and Diehl (PRIOS) meters

Version 0.12.0 got these two wrong: a Techem heat cost allocator reported nonsense under names like VIF_RETURN_TEMP, a Techem heat meter could not be read at all and ended up on the automatic block list, and a PRIOS water meter reported its volume as a heat cost unit. 0.12.1 reads them correctly again - the states 0.12.0 created for them stay behind and can be deleted, the right ones are written with the next telegram.

Two of their states are named differently than in 0.11.x, because the values of the previous period now carry the storage number they belong to (1-1-… rather than 1-0-…), and the remaining battery life of a PRIOS meter is reported in months rather than in years.

ToDo

  • sending telegrams for S mode receivers?
  • handling meters with "multiple telegrams"

Changelog

0.14.1 (2026-10-21)

  • (ChL) Fix the CUL receiver leaving the LQI at the end of every telegram: culfw appends the LQI and the RSSI, but only the RSSI was taken off. Telegrams of frame type B then failed with "Telegram data is too short!" and the meter was blocked after 10 of them (#325)

0.14.0 (2026-09-26)

  • (ChL) Every data state remembers the data record it was created for, and a value of a different record at the same position of a telegram is skipped instead of being written to it
  • (ChL) Name, unit and role of the data states follow the decoded telegram as long as nobody changed them; names you gave a state and states of earlier versions stay as they are, and the new option "Overwrite name, unit and role of the data states" sets them all back
  • (ChL) Fix the states of data records that a device did not have in its first telegram after a start of the adapter: they were written without being created
  • (ChL) Fix states, and devices, that were deleted in the object tree while the adapter was running: they were written without an object until the next start, and are created again by the next telegram now
  • (ChL) Count the telegram variants of every device - the layouts of data records a meter sends - and show them in the new "Telegram variants" tab of the admin UI
  • (ChL) Telegram variants can be ignored per device, so that the values of a layout nobody wants are not written; the log says at the start which entries of the list can never match

0.13.1 (2026-09-22)

  • (ChL) Convert the adapter to TypeScript
  • (ChL) Fix the "Simple Hexstring" receiver rejecting a telegram that carries its block CRCs without announcing them with a leading "Z": the parser is left to look for them rather than being told there are none, which made it read the first CRC byte as the CI field (#276)
  • (ChL) The "Simple Hexstring" receiver drops a line that is no telegram instead of turning it into one, takes a lower case "z" as the CRC marker as well, and reports the frame type of the configured mode again
  • (ChL) Fix the instance reporting the connection of the receiver as its connection to the ioBroker databases: a receiver that was away showed an instance as disconnected that was talking to them, and a connected one kept js-controller from stopping an adapter that had lost the states database

0.13.0 (2026-09-09)

  • (ChL) Describe the manufacturer specific data records of a meter in the admin UI, the result become states of their own
  • (ChL) Update wireless-mbus-parser to 1.5.0: support for decoding manufacturer specific blobs - description for Itron smoke detector included.

0.12.3 (2026-09-07)

  • (ChL) Fix the CUL initialisation on the receivers that lose the first byte written after the line has been idle: every command is now sent with a separator in front of it, which is what gets lost instead of the command letter, and setting the mode waits for its confirmation rather than failing on a line that crossed it (#312)

License

Copyright (c) 2019 ISFH - Institute for Solar Energy Research www.isfh.de
Copyright (c) 2021 - 2026 Christian Landvogt

Licensed under GPLv2. See LICENSE and NOTICE