Giter VIP home page Giter VIP logo

pymeterbus's Introduction

Meter-Bus for Python

Build status codecov pypi GitHub issues GitHub issues PyPI Status

About

M-Bus (Meter-Bus) is a European standard (EN 13757-2 physical and link layer, EN 13757-3 application layer) for the remote reading of gas or electricity meters. M-Bus is also usable for other types of consumption meters. The M-Bus interface is made for communication on two wires, making it very cost-effective.

Python version

I've decided only to support active Python version. Thus, any EOL version is not supported.

Current State (2022)

The library works, but it lacks proper documentation. Well, it lacks any documentation, to be honest.

The implementation is currently under heavy development. Its original intended use case was particular, as a library to aid in decoding M-Bus telegrams sent over HTTP, and might thus not suit everyone.

Still, it is a generic library and supports several different use cases.

  • Decoding of re-encoded M-Bus frames sent from an Elvaco Wireless M-Bus master over HTTP.
  • Communicating with M-Bus devices over RS-232 serial.
  • Communication with M-Bus devices over RFC 2217.
  • As a debugging tool to decode M-Bus frames.

Currently, only the variable data structures are implemented. The library can only decode M-Bus frames. It does presently NOT support encoding and transmission of M-Bus frames, such as control frames.

However, if the need arises, I might implement missing pieces on a request basis.

Tools

You can find a set of utilities in the tools folder.

  • mbus-serial-request-data.py
  • mbus-serial-request-data-multi-reply.py
  • mbus-serial-scan.py
  • mbus-serial-scan-secondary.py

These tools can communicate over a serial device /dev/ttyX or even over RFC2217 using the format rfc2217://host:port.

Suppose you are using ser2net as an RFC2217 server. You need to configure it the following way:

2000:telnet:0:/dev/ttySX:2400 remctl banner

Known Issues

  • Missing: Fixed data structure parsing.
  • Missing: Encoding to M-Bus frames.
  • Missing: Slave configuration.
  • Missing: Extended VIF codes.

What works

  • Querying an M-Bus device over serial.
  • Parsing of a complete telegram.
  • Parsing of just the Used Data segment.
  • Generating a basic JSON structure from the telegram/user-data/records.

Basic API documentation

meterbus.load(data)

  • data[str]: M-Bus frame data

Returns an object of either type WTelegramSndNr, TelegramACK, TelegramShort, TelegramControl or TelegramLong. If an error occurs, it will raise an MBusFrameDecodeError.

meterbus.debug(state)

  • state[bool]: set the global debug state

Produces debug messages to stdout.

meterbus.send_ping_frame(ser, address)

  • ser[pySerial connection]: an open pySerial object
  • address: The target's primary address

Sends a PING frame to address over the serial connection ser.

meterbus.recv_frame(ser, length)

  • ser[pySerial connection]: an open pySerial object
  • length: The minimum length of the reply. An ACK frame is one (1) byte.

Reads an entire frame and returns the unparsed data.

meterbus.send_request_frame_multi(ser, address, req)

  • ser[pySerial connection]: an open pySerial object
  • address: The target's primary address

If req is None, build a new request frame using address and send it.

meterbus.send_select_frame(ser, secondary_address)

  • ser[pySerial connection]: an open pySerial object
  • secondary_address[str]: A target using secondary address format

Sends a select frame with the supplied secondary address.

meterbus.XXX

More to come...

Code examples

Decode the value of a single record (record 3)

#!/usr/bin/python

import meterbus

data = "\x68\x6A\x6A\x68\x08\x01\x72\x43\x53\x93\x07\x65" \
       "\x32\x10\x04\xCA\x00\x00\x00\x0C\x05\x14\x00\x00" \
       "\x00\x0C\x13\x13\x20\x00\x00\x0B\x22\x01\x24\x03" \
       "\x04\x6D\x12\x0B\xD3\x12\x32\x6C\x00\x00\x0C\x78" \
       "\x43\x53\x93\x07\x06\xFD\x0C\xF2\x03\x01\x00\xF6" \
       "\x01\x0D\xFD\x0B\x05\x31\x32\x4D\x46\x57\x01\xFD" \
       "\x0E\x00\x4C\x05\x14\x00\x00\x00\x4C\x13\x13\x20" \
       "\x00\x00\x42\x6C\xBF\x1C\x0F\x37\xFD\x17\x00\x00" \
       "\x00\x00\x00\x00\x00\x00\x02\x7A\x25\x00\x02\x78" \
       "\x25\x00\x3A\x16"

telegram = meterbus.load(data)
print telegram.records[3].parsed_value
~$ 2014-02-19T11:18

Request a frame over Serial and dump it in JSON format

#!/usr/bin/python

import serial
import meterbus

address = 254

with serial.Serial('/dev/ttyACM0', 2400, 8, 'E', 1, 0.5) as ser:
  meterbus.send_ping_frame(ser, address)
  frame = meterbus.load(meterbus.recv_frame(ser, 1))
  assert isinstance(frame, meterbus.TelegramACK)

  meterbus.send_request_frame(ser, address)
  frame = meterbus.load(meterbus.recv_frame(ser, meterbus.FRAME_DATA_LENGTH))
  assert isinstance(frame, meterbus.TelegramLong)

  print(frame.to_JSON())

M-Bus Packet Format

Single Character Short Frame Control Frame Long Frame
E5h Start 10h Start 68h Start 68h
C Field L Field = 3 L Field
A Field L Field = 3 L Field
Check Sum Start 68h Start 68h
Stop 16h C Field C Field
A Field A Field
CI Field CI Field
Check Sum User Data (0-252 Byte)
Stop 16h Check Sum
Stop 16h

License

Please see the LICENSE file

pymeterbus's People

Contributors

blagasz avatar blazyy avatar cdu-genedis avatar chrisrutherfordsw2 avatar dependabot[bot] avatar ganehag avatar ganehag-noda avatar geronet1 avatar mexx42 avatar nobodyguy avatar wolfgangfahl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pymeterbus's Issues

how to send a different data frame

Ive got an Eastron energy measuring module.
With the standard mbus-serial-request-data and mbus-serial-request-data-multi-reply I only get the Energy values.

The protocol (https://www.kwhmeter.nl/Files/2/26000/26485/Attachments/Product/0Oq90EEQ42226h7911o5983365exE52r.pdf) states that when I send the following frame:
Master to slave : 68 03 03 68 53 XX b1 05 16
Slave to master: Variable data structure (instantaneous electrical information)
If the primary address is 01 then XX=01

But how do I send this frame to the slave?

Any tips?

add google doc strings and type hints to code

ChatGPT4 will happily assist in this e.g.

def serial_send(ser: serial.Serial, data: bytes = None, read_echo: bool = False) -> None:
    """
    Sends data through the serial interface and optionally reads echo.

    :param ser: Serial port instance.
    :param data: Data to be sent as bytes.
    :param read_echo: Flag to determine if the echo should be read.
    """

Creating SND_UD long frames

I could not figure out how to create and SND_UD long frame. The docs say:

6.4.2 Writing Data to a Slave
The master can send data to a slave using a SND_UD with CI-Field 51h for mode 1 or 55h for
mode 2. Note that the data structure in such a write telegram has been changed in contrast to
previous definitions by means of leaving out the fixed data header of 12 byte.

Is it possible to do that with the current implementation? As far as I understands it needs a 13 bit header unless it starts with 0x78. I am not sure if I modify the code to allow 0x51 as well it won't break it somewhere else.

Thanks for your help!

IndexError: list index out of range

Getting the following errors:

File "/home/.local/lib/python3.7/site-packages/meterbus/telegram_body.py", line 196, in load
    self.version_field = bodyHeader[7]
IndexError: list index out of range

When parsing "68 05 05 68 08 00 78 0f 00 8f 16"

MBusFrameDecodeError: ('empty frame', None)

Hi

I run this code and i get MBusFrameDecodeError: ('empty frame', None)

#!/usr/bin/python
import serial
import meterbus
address = 254
serial_port = 'COM3'

try:
with serial.Serial(serial_port, 115200, bytesize=8, parity='N', stopbits=1, timeout=0.5) as ser:
meterbus.send_ping_frame(ser, address)
frame = meterbus.load(meterbus.recv_frame(ser, 1))
assert isinstance(frame, meterbus.TelegramACK)

    meterbus.send_request_frame(ser, address)
    frame = meterbus.load(meterbus.recv_frame(ser, meterbus.FRAME_DATA_LENGTH))
    assert isinstance(frame, meterbus.TelegramLong)

    print(frame.to_JSON())

except serial.SerialException as e:
print(f"Could not open serial port {serial_port}: {e}")

Is ENUM34 a required dependency?

I'm trying to use pyMeterBus for Home Assistant integration.
https://developers.home-assistant.io/docs/en/creating_integration_manifest.html

However I get the following error when I try to run the component:

[homeassistant.util.package] Unable to install package enum34==1000000000.0.0: Could not find a version that satisfies the requirement enum34==1000000000.0.0 (from -c /home/mikhail/work/home-assistant/homeassistant/package_constraints.txt (line 22)) (from versions: 0.9, 0.9.1, 0.9.11, 0.9.12, 0.9.13, 0.9.14, 0.9.15, 0.9.16, 0.9.17, 0.9.18, 0.9.19, 0.9.20, 0.9.21, 0.9.22, 0.9.23, 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.1.0, 1.1.1, 1.1.2, 1.1.3, 1.1.4, 1.1.5, 1.1.6)

I can also see in the configuration file of home assistant there is:

# Breaks Python 3.6 and is not needed for our supported Python versions
enum34==1000000000.0.0

Was wondering if that can be removed from requirements.txt

Fixed requirements.txt

Hi there,

within requirements.txt there are some fixed modules like

pycryptodome==3.7.3
pyserial==3.4

Since we have Python 3.9 / 3.10 currently and some libs have been updated may it be possible to limit these to be newer then required like

pycryptodome>=3.7.3
pyserial>=3.4

Can we change to accept 115200 baud rate?

Hi,

Your pure Python implementation is very robust and well thought out. We would like to use this for a device running a baud rate of 115.2k.

Do you know if your code can be configured by us to accept a higher baud rate than your upper limit of 38400? In particular, it's the constants for the hex values that we set for 115200 baud. The code uses 300 = 0xB8, up to 38400 = 0xBF. Do you have any light you could shed on a hex value for 115200 as I tried 0xC1 and got an error. Sorry for my ignorance it seems but I havent been able to find the equivalent hex value for 115200 anywhere in the MBUs standards.

Any advice you could provide would be great! Thanks .

help decoding SND_NR

Hi guys I'm trying to decode this frame
20 44 2D 2C 97 18 58 68 1D 16 8D 20 55 82 E2 F5 23 CC EE ED C2 B1 1C 50 35 0B EA CE 7E 01 D0 3A AD
all the libraries I tested until now gives me incoherent data.
I cannot find the right spec for CI-Field = 0x8D [0x8D: 'Extended Link Layer II (8 Byte)']
I'm pretty sure byte 12 (55) is a sequence number as it increase by one each new message.
Then I'm lost when I try to decode this part:
82 E2 F5 23 CC EE ED C2 B1 1C 50 35 0B EA CE 7E 01 D0 3A AD
Are we looking for a header ? Is 82 a new CI-Field ? [0x82: 'For future use',]

My meter is a kamstrup flow IQ 3100 and it should use 128bits AES encryption.
I don't think this library supports encrypted data but if I can get the encrypted payload, I'll be able to fork and add encryption support.

I hope you can help me ...
Regards.

Latest version not published to PyPI

Currently, when trying to install via PIP, it errors out due to the fact there is no VERSION file in the archive. I can see that there is a VERSION file in the repo which was created more recently than the latest package in PyPI (Nov 23 18).

Would it be possible to publish the latest version to PyPI?

Unable to install on Windows

Windows doesn't allow any files to have the name "aux" regardless of extension, so pyMeterBus fails to install on Windows. Renaming aux.py and updating all references to the new name allows the install to complete.

Once installed pyMeterBus works nicely on Windows although I've only tested it with RFC2217 rather than directly attached devices.

Would be nice to add a prefix to the request

It looks like my meter replies to the command only if its sent with a prefix of 210:

\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x5b\x00\x5b\x16

instead of \x10\x5b\x00\x5b\x16 sent by: meterbus.send_request_frame(ser, address)

Was wondering if there is a way to add this prefix? For now I run:

ser.write(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x5b\x00\x5b\x16')
frame = meterbus.load(meterbus.recv_frame(ser))
print(frame.to_JSON())

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.