Skip to content

Instantly share code, notes, and snippets.

@kenzodeluxe
Last active November 19, 2024 17:36
Show Gist options
  • Save kenzodeluxe/464f2b6b4f810420fabb2f0251b4e913 to your computer and use it in GitHub Desktop.
Save kenzodeluxe/464f2b6b4f810420fabb2f0251b4e913 to your computer and use it in GitHub Desktop.
Read values from Landis & Gyr T230/330 / Qundis Qheat 5.5 heat meters
#!/usr/bin/python3
"""
Script to read values from Qundis Qheat 5.5 (which seems to be "identical" to Landis&Gyr T230/T330)
It's necessary to send 210 leading zeros before the actual request
Check https://www.sipatec.rs/files/uploads/T230.pdf for further protcol details (and subsequent messages)
Uses https://github.com/ganehag/pyMeterBus to decode M-BUS datagrams
"""
import binascii
import datetime
import json
import logging
import sys
from os import getenv
from time import sleep
import serial
sys.path.append('/home/pi/pyMeterBus')
import meterbus
from influxdb import InfluxDBClient
error_sleep_interval = 10
loglevel = getenv('LOGLEVEL', 'INFO').upper()
# InfluxDB
send_influx = bool(getenv('SEND_INFLUX', True))
dbclient = InfluxDBClient(getenv('INFLUX_HOST'),
getenv('INFLUX_PORT', 8086),
getenv('INFLUX_USER', ''),
getenv('INFLUX_PASS', ''),
getenv('INFLUX_DB', 'wmz'))
json_body = []
receiveTime = datetime.datetime.utcnow()
lookup_map = { 2: 'VIFUnit.ENERGY_WH',
3: 'VIFUnit.VOLUME'}
# Serial
serial_port = getenv('SERIAL_PORT', '/dev/ttyUSB0')
ser = serial.Serial(serial_port, baudrate=2400, bytesize=8, parity="E", stopbits=1, timeout=2, xonxoff=0, rtscts=0)
def write_zeros():
i = 210
while i:
ser.write(b'\x00')
i = i - 1
def send_status_request():
write_zeros()
ser.write(b'\x68\05\x05\x68\x53\xFE\x51\x0F\x0F\xC0\x16')
# see link from above to understand the actual values
try:
frame = meterbus.load(meterbus.recv_frame(ser))
logging.debug(json.dumps(json.loads(frame.body.to_JSON()), indent=4, sort_keys=True)) # there might be an easier way
return True
except Exception as e:
logging.error(f'Unable to decode datagram, please retry: {e}')
return False
def get_data():
write_zeros()
ser.write(b'\x10\x7b\xFE\x79\x16')
try:
frame = meterbus.load(meterbus.recv_frame(ser))
json_response = json.loads(frame.body.to_JSON()) # there might be an easier way
return json_response
except Exception as e:
logging.error(f'Unable to parse data from WMZ: {e}')
return False
def main():
retries = getenv('RETRIES', 5)
while retries:
success = send_status_request()
if not success:
logging.error('WMZ did not correctly respond to status request. Retrying.')
sleep(error_sleep_interval)
retries -= 1
continue
wmz_data = get_data()
if wmz_data:
logging.debug(wmz_data)
# correct datagram should show this as its first entry (Qheat 5.5)
if wmz_data['records'][0]['type'] != 'VIFUnit.ACTUALITY_DURATION':
logging.error('Incorrect datagram, retrying.')
sleep(error_sleep_interval)
continue
else:
for key in lookup_map.keys():
influx_entry = { "measurement": "", "time": receiveTime, "fields": { "value": "" }}
try:
influx_entry['measurement'] = lookup_map[key].strip()
influx_entry['fields']['value'] = wmz_data['records'][key]['value']/1000
json_body.append(influx_entry)
except Exception as e:
logging.error(f'Unable to assign values from WMZ data to InfluxDB request data: {e}')
sleep(error_sleep_interval)
continue
if send_influx:
dbclient.write_points(json_body)
logging.debug(json_body)
print('Done.')
return
else:
retries -= 1
sleep(error_sleep_interval)
continue
logging.error(f'Did not get a meaningful response within {retries} retries. Please re-run later.')
if __name__ == "__main__":
main()
@pascal-t
Copy link

Hi thank you for the script. I tried this code on my Metrona Ultraheat XS 2, which is also Identical to the Landis + Gyr T230, and it worked perfectly. However I have to press the button to wake it up.
Did you get it to work without pressing the button?

@kenzodeluxe
Copy link
Author

For me, it works fine without pressing a button - the device will wake up using the script.
If that's not the case for you, try finding software from Metrona and checking the communication (e.g. using Wireshark) should that software wake up your device without your manual interaction. Good luck!

@pascal-t
Copy link

pascal-t commented Nov 4, 2022

Thanks for the reply. I created a ticket asking if they provide such a software, but I won't get my hopes up.
In the meantime I tinkered around with the script and got it to work by sending more zeros before the sending the status request. Currently with 500 it works on the second try. I'm going to try some other values until I can reliably read it on the first try.

How did you determine the oddly specific number of 210 zeros? I did not find anything about it in the documentation you linked.
Also how regularly do you run the script? I don't want to explain to my gas provider / measuring service provider that I depleted the battery by spamming the optical interface.

@kenzodeluxe
Copy link
Author

How did you determine the oddly specific number of 210 zeros?

I am almost certain I used Landis&Gyr UltraAssist to connect to the device, and inspected that traffic (communication on ttyUSB*) to find out what was needed for my device. At the time, I did not find any script to perform successful readings from that specific heat meter.

Also how regularly do you run the script?

I run it on a daily basis, and the device will keep on/active for a few minutes after the successful read; however, since I own it (I use it for my heat pump), depleting the battery isn't a huge concern for me (I know I'll have to replace it eventually).

@Dani1802
Copy link

Hi @kenzodeluxe
I have a Qundis Heat 5.5 and am struggling reading the values with your script.

Depending on the position of my reader, the error is either:

  • ERROR:root:Unable to decode datagram, please retry: ('empty frame', False)

or:

  • ERROR:root:Unable to decode datagram, please retry: ('empty frame', None)

Any Ideas what could be wrong?
Which reader do you use?

I'm using this reader (https://www.amazon.de/gp/product/B01B8N0ASY) connected to a Pi 3 B+.

Thank you and regards,
Daniel

@kenzodeluxe
Copy link
Author

Hi Daniel,

Any Ideas what could be wrong? Which reader do you use?

I use some no-name reader I got off of eBay, it's recognized as Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO). Did you give the official Landis&Gyr software a try yet? Or did you try that reader on any other device, like a power meter, for comparison?

@Dani1802
Copy link

Hi @kenzodeluxe,
I tried to download Landis+Gyr UltraAssist, but unfortunately an account is needed which I don't have.
Yes, the reader was working fine with my powermeter.

@KernSani
Copy link

Hi,
I know I'm late to the party, however: I tried to recreate your script in Tasmota (I just have an wireless ESP32 reader available), however I didn't succeed. I'm not sure if I'm sending the correct initial hexstring. In the comments you mention "# see link from above to understand the actual values". Unfortunately I can't find anything regarding those values... Could you elaborate a bit on that? I also noticed the hexstring seems to miss an "x" ("\x68\05\x05...) which also confused me a bit. Would be great if someone is still looking into this. Any help is appreciated :-)
Thanks a lot,
regards,
Oli

@kenzodeluxe
Copy link
Author

Unfortunately I can't find anything regarding those values...

I agree - looks like the PDF was replaced. I found the original one I was referencing here.

I also noticed the hexstring seems to miss an "x" ("\x68\05\x05...) which also confused me a bit.

You're right - I just changed this locally and it doesn't seem to make a difference. Likely, the meter doesn't care about the full string that much, however adding the x didn't break communication for me.
Does the script itself work with your meter? Are you using it with a Qundis Qheat 5.5 as well?
I haven't changed the script in years and it still runs fine on my machine :)

@KernSani
Copy link

Hi,
thanks for the quick response. The referenced document helped a lot to understand what I'm doing, but it doesn't look like I'm doing anything wrong. Unfortunately I can't test your script directly, because I only have that ESP32 reader (can't run python), so I tried to rebuild it in Tasmota. I'll get another (USB) reader end of the week, then I can try with a raspberry pi/python. My meter is a T330, but I understand it is similar to the T230.
For now, I think I didn't even manage to wake up the meter... I'll try with different numbers of zeroes now. Let's see if that helps (otherwise I'll verify that your script works, once the reader arrives)
Thanks,
Oli

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment