mtdcr / pysml

Python library for EDL21 smart meters using Smart Message Language (SML)
MIT License
16 stars 8 forks source link

Support for Iskra MT382 possible? #4

Closed Bascht74 closed 2 years ago

Bascht74 commented 3 years ago

Hallo, ich bin begeisterter Benutzer von Home Assistant und benutze auch die EDL21-Integration. Mein Stromanbieter hat mir zwei Zähler installiert, einen MT175 (für die PV-Anlage) und einen MT382 (da ich einen "Smart-Tarif" habe, bei dem man zu verschiedenen Zeiten unterschiedliche Tarife hat. Er hat 8 Zählwerke). Den MT175 habe ich sofort eingebunden bekommen. Erstmal vielen Dank für diese tolle Möglichkeit! Einfach USB-Leser anschließen, konfigurieren und schon werden die Werte geliefert. Einfacher geht es fast nicht...

Der Iskra MT382 ist aber eine andere Sache. Dieser spricht mit dem Protokoll IEC 62056-21, D0 als Klarschrift und unverschlüsselt).

Dazu muss er allerdings mit HEX 2F 3F 21 0D 0A ("/?!") bei 300 Baud/7E1 angesprochen werden. Danach meldet sich der Zähler mit "/ISK5\2MT382-1008<\r>" und dann kann man mit hex 06 30 30 30 0D 0A ("000" oder mit "hex 06 30 35 30 0D 0A" (Umschaltung auf 9600 Baud) die Werte anfordern.

Danach gibt er alle Werte im Klartext aus: 0-0:96.1.1(314xxxxxxxxxxxxxxxxxxxxx37)<\r><\n> 0-0:128.20.0(016)<\r><\n> 1-0:0.9.2(210528)<\r><\n> 1-0:0.9.1(215658)<\r><\n> 1-0:1.7.0(001.322kW)<\r><\n> 1-0:2.7.0(000.000kW)<\r><\n> 1-0:1.8.0(010192.57kWh)<\r><\n> 1-0:1.8.1(006094.31kWh)<\r><\n> 1-0:1.8.2(001410.19kWh)<\r><\n> 1-0:1.8.3(000981.30kWh)<\r><\n> 1-0:1.8.4(001706.76kWh)<\r><\n> 1-0:1.8.5(000000.00kWh)<\r><\n> 1-0:1.8.6(000000.00kWh)<\r><\n> 1-0:1.8.7(000000.00kWh)<\r><\n> 1-0:1.8.8(000000.00kWh)<\r><\n> 1-0:1.2.0(106.732kW)<\r><\n> 1-0:1.6.0(007.072kW)<\r><\n> 1-0:2.8.0(006715.38kWh)<\r><\n> 1-0:3.8.0(000122.04kvarh)<\r><\n> 1-0:4.8.0(006255.99kvarh)<\r><\n> 1-0:0.2.2(Smart001)<\r><\n> 1-0:0.3.0(00500)<\r><\n> 1-0:0.3.1(00500)<\r><\n> 1-0:0.3.3(250)<\r><\n> 0-0:97.97.0(00000000)<\r>

Weitere Informationen habe ich hier gefunden: https://wiki.volkszaehler.org/hardware/channels/meters/power/edl-ehz/iskraemeco_mt372 Dort geht es zwar um den MT372, aber der MT382 funktioniert ganz genau so...

Wäre es möglich, auch diesen Zähler zu unterstützen, z.B. indem über die o.a. Strings die Zählerdaten angefordert werden, wenn ein EDL-Zähler z.B. nach 5 Sekunden noch keine Daten geliefert hat?

Ich würde mich sehr freuen, wenn ich auch meinen Hauptzähler integrieren könnte, u.a. da ich in kürze eine OpenWB (Wallbox) bekomme, die PV-geführt mein E-Auto aufladen soll :-) Das soll natürlich primär bei Sonneneinstrahlung passieren (PV = 13 Cent pro KWh) und falls nicht möglich dann nachts, wenn der Strom am günstigsten ist (bei mir 19 Cent pro KWh). Dafür benötige ich blöderweise diese Zählerdaten in HA, damit das gut funktioniert...

Liebe Grüße,

Sebastian

image


Hello, I am an user of Home Assistant and also use the EDL21 integration. My electricity provider installed two smart meters for me, an MT175 (for the PV system) and an MT382 (since I have a "smart tariff" where you have different rates at different times. the meter has 8 counters). I got the MT175 integrated immediately. First of all, thank you for your great work! Just connect the USB reader, configure it via YAML and the values are delivered. It almost couldn't be easier...

But the Iskra MT382 is less or more a problem. This one speaks with the protocol IEC 62056-21, D0 as plain text and unencrypted.

But it has to be addressed with HEX 2F 3F 21 0D 0A ("/?!") at 300 baud/7E1. After that the meter answers "/ISK5\2MT382-1008<\r>" and then you can request the values with hex 06 30 30 0D 0A ("000" or with "hex 06 30 35 30 0D 0A", "050" (switching to 9600 baud).

Then it outputs all values in plain text: 0-0:96.1.1(314xxxxxxxxxxxxxxxxxxxxx37)<\r><\n> 0-0:128.20.0(016)<\r><\n> 1-0:0.9.2(210528)<\r><\n> 1-0:0.9.1(215658)<\r><\n> 1-0:1.7.0(001.322kW)<\r><\n> 1-0:2.7.0(000.000kW)<\r><\n> 1-0:1.8.0(010192.57kWh)<\r><\n> 1-0:1.8.1(006094.31kWh)<\r><\n> 1-0:1.8.2(001410.19kWh)<\r><\n> 1-0:1.8.3(000981.30kWh)<\r><\n> 1-0:1.8.4(001706.76kWh)<\r><\n> 1-0:1.8.5(000000.00kWh)<\r><\n> 1-0:1.8.6(000000.00kWh)<\r><\n> 1-0:1.8.7(000000.00kWh)<\r><\n> 1-0:1.8.8(000000.00kWh)<\r><\n> 1-0:1.2.0(106.732kW)<\r><\n> 1-0:1.6.0(007.072kW)<\r><\n> 1-0:2.8.0(006715.38kWh)<\r><\n> 1-0:3.8.0(000122.04kvarh)<\r><\n> 1-0:4.8.0(006255.99kvarh)<\r><\n> 1-0:0.2.2(Smart001)<\r><\n> 1-0:0.3.0(00500)<\r><\n> 1-0:0.3.1(00500)<\r><\n> 1-0:0.3.3(250)<\r><\n> 0-0:97.97.0(00000000)<\r>

I found more information here: https://wiki.volkszaehler.org/hardware/channels/meters/power/edl-ehz/iskraemeco_mt372 It is about MT372, but MT382 works quite in the same way....

Would it be possible to support this counter as well, e.g. by requesting the meter data via the strings mentioned above, e.g. if an smart meter has not delivered any data since 5 seconds?

I would be very happy if I could also integrate my main smart meter as well, because I will soon get an OpenWB (cool wallbox), which is PV-led to charge my e-car :-) This should of course happen primarily when the sun is shining (PV = 13 cents per KWh) and if not possible then at night when the electricity is cheapest (19 cents per KWh for me). This is why I would need that data in HA ... :-(

Kind regards,

Sebastian

Bascht74 commented 3 years ago

I found out that the dsmr integration might be an alternative. But I was not successful. I opened an issue here and asked if he could support the initialization of the optical interface: https://github.com/ndokter/dsmr_parser/issues/81

What I found as well: https://github.com/home-assistant/core/issues/37349 my meter is the successor of the following: https://wiki.volkszaehler.org/hardware/channels/meters/power/edl-ehz/iskraemeco_mt372

I am really interested in getting it done because I want to use the meter for my wallbox in a couple of weeks... I really hope that 37349 can be continued and a solution can be found. I have the meter, an optical probe and HTerm ready at hand ... :-)

Sebastian

@zibous Could you be so kind and share your script? It is not available anymore on pastebin

zibous commented 3 years ago

@Bascht74 i use a simple python script to send the data to the mqtt brocker:

#!/usr/bin/python3

## requirements
## paho_mqtt==1.5.0
## pyserial==3.4
## python-dotenv==0.14.0

# Reading a meter using a optical usb probe via the D0-interface.
import serial
import time
from datetime import datetime
import json
import paho.mqtt.publish as publish
import logging
import os.path
import re
from dotenv import load_dotenv, find_dotenv
import csv
from io import StringIO

# .env file settings
# SERIALPORT = '/dev/ttyUSB0'
# BAUDRATE = 300
# TIMEOUT = 10
# MQTTAUTH = {'username': '******', 'password': '********'}
# MQTTHOST = 'localhost'
# MQTTPORT = 1883
# MQTTCLIENT = 'ISK5MT174-DATA'
# MQTTTOPIC = 'tele/ISK5MT174'
# DEVICEID = 'ISk5MT174-0001'
# LOGGERON = True

load_dotenv(find_dotenv())

MQTTAUTH = {}
SERIALPORT = os.environ.get("SECRET_SERIALPORT")
BAUDRATE = int(os.environ.get("SECRET_BAUDRATE"))
TIMEOUT = int(os.environ.get("SECRET_TIMEOUT"))
MQTTAUTH['username'] = os.environ.get("SECRET_MQTTUSER")
MQTTAUTH['password'] = os.environ.get("SECRET_MQTTPASSWORD")
MQTTHOST = os.environ.get("SECRET_MQTTHOST")
MQTTPORT = int(os.environ.get("SECRET_MQTTPORT"))
MQTTCLIENT = os.environ.get("SECRET_MQTTCLIENT")
MQTTTOPIC = os.environ.get("SECRET_MQTTTOPIC")
DEVICEID = os.environ.get("SECRET_DEVICEID")
# LOGGERON = bool(os.environ.get("SECRET_LOGGERON"))
LOGGERON = False

# dateTimeObj = datetime.now().strftime('%Y-%m')
DATAFILE = os.path.dirname(__file__) + '/' + DEVICEID + '.json'

# logger settings
logger = logging.getLogger(__name__)
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s,%(msecs)d %(levelname)s: %(message)s",
    datefmt="%H:%M:%S",
)

if(LOGGERON):
    logger.info(f"Datafile: {DATAFILE}")

class ArgsToCsv:
    def __init__(self, seperator=","):
        self.seperator = seperator
        self.buffer = StringIO()
        self.writer = csv.writer(self.buffer)

    def stringify(self, *args):
        self.writer.writerow(args)
        value = self.buffer.getvalue().strip("\r\n")
        self.buffer.seek(0)
        self.buffer.truncate(0)
        return value + "\n"

def __savedata__(header, datastring):

    # write the historydata
    file_path = os.path.dirname(__file__) + '/' + datetime.now().strftime('%Y-%m') + '-data.csv'

    if os.path.isfile(file_path):
        addHeader = False
    else:
        addHeader = True

    try:
        with open(file_path, 'a+') as f:
            if addHeader:
                f.write(header + '\n')
            f.write(datastring)

    except Exception as e:
        logger.error(f"Error {__name__}: {str(e)},  line {sys.exc_info()[-1].tb_lineno}")
        pass

# serial port connection weidmann IR Schreib/Lesekopf USB (Optokopf)
# IC: FTDI FT232R / FT230X USB-to-UART Serial Converter
# Baudrates: 300 Baud bis 28800 Baud
# UART interface support für 7 oder 8 data bits, 1 oder 2 stop bits und odd / even / mark / space / no parity
# Unterstützte Normen: DIN EN 62056-21, IEC-62056-21

def main():

    if(LOGGERON):
        logger.info(
            f"Init Serial Interface: Port: {SERIALPORT}, Baudrate: {BAUDRATE}, Bits: {serial.SEVENBITS}, Parity; {serial.PARITY_EVEN}, Timeout: {TIMEOUT}")

    ser = serial.Serial(SERIALPORT, BAUDRATE, serial.SEVENBITS, serial.PARITY_EVEN, timeout=TIMEOUT)

    # enable optical port for data transfer
    if(LOGGERON):
        logger.info(f"enable optical port")

    ser.write(b'\x2F\x3F\x21\x0D\x0A')
    time.sleep(0.5)
    numberOfLine = 0

    # default settings
    data = {}
    dateTimeObj = datetime.now()
    data['device'] = 'Stromzähler Haus'
    data['deviceid'] = DEVICEID
    data['date'] = ''
    data['time'] = ''

    # current displaymeter data
    data['energy_consumption_meter_ht'] = 0.000
    data['energy_consumption_meter_nt'] = 0.000
    data['energy_production_meter_t1'] = 0.000
    data['energy_production_meter_t2'] = 0.000

    # delta current active and negative power
    data['energy_consumption_ht'] = 0.000
    data['energy_consumption_nt'] = 0.000
    data['energy_consumption'] = 0.000

    data['energy_production_t1'] = 0.000
    data['energy_production_t2'] = 0.000
    data['energy_production'] = 0.000

    # day sum active and negative power
    data['energy_consumption_day_ht'] = 0.000
    data['energy_consumption_day_nt'] = 0.000
    data['energy_consumption_day'] = 0.000

    data['energy_production_day_t1'] = 0.000
    data['energy_production_day_t2'] = 0.000
    data['energy_production_day'] = 0.000

    # month sum active and negative power
    data['energy_consumption_month_ht'] = 0.000
    data['energy_consumption_month_nt'] = 0.000
    data['energy_consumption_month'] = 0.000

    data['energy_production_month_t1'] = 0.000
    data['energy_production_month_t2'] = 0.000
    data['energy_production_month'] = 0.000

    # year sum active and negative power
    data['energy_consumption_year_ht'] = 0.000
    data['energy_consumption_year_nt'] = 0.000
    data['energy_consumption_year'] = 0.000

    data['energy_production_year_t1'] = 0.000
    data['energy_production_year_t2'] = 0.000
    data['energy_production_year'] = 0.000

    # total sum active and negativ power
    data['energy_consumption_total'] = 0.000
    data['energy_production_total'] = 0.000
    data['energy_total'] = 0.000

    data['unit_of_measurement'] = 'kWh'
    data['icon'] = 'mdi:billboard'
    data['timestamp'] = dateTimeObj.strftime('%Y-%m-%d %H:%M:%S.%f')
    data['periode'] = dateTimeObj.strftime('%Y-%m-%d')
    data['month'] = dateTimeObj.strftime('%Y-%m')
    data['year'] = dateTimeObj.strftime('%Y')
    data['version'] = '1.0.0'
    data['status'] = 'INIT'
    data['attribution'] = 'Data provided by Peter Siebler'

    # reads previuos data back
    if os.path.isfile(DATAFILE):
        if(LOGGERON):
            logger.info(f"get previous data")

        with open(DATAFILE, "r") as f:
            _prevdat = f.read()

        # decoding the JSON to dictionay
        prevData = json.loads(_prevdat)

        # reset data if the day changed
        if(prevData['periode'] != data['periode']):
            prevData['energy_consumption_day_ht'] = 0.000
            prevData['energy_consumption_day_nt'] = 0.000
            prevData['energy_consumption_day'] = 0.000
            prevData['energy_production_day_t1'] = 0.000
            prevData['energy_production_day_t2'] = 0.000
            prevData['energy_production_day'] = 0.000

        # reset data if the month changed
        if(prevData['month'] != data['month']):
            prevData['energy_consumption_month_ht'] = 0.000
            prevData['energy_consumption_month_nt'] = 0.000
            prevData['energy_consumption_month'] = 0.000
            prevData['energy_production_month_t1'] = 0.000
            prevData['energy_production_month_t2'] = 0.000
            prevData['energy_production_mont'] = 0.000
            # save the report data file for the previous month
            reportfile = os.path.dirname(__file__) + '/' + prevData['month'] + '-data.json'
            with open(reportfile, "w") as f:
                f.write(_prevdat)

         # reset data if the year changed
        if(prevData['year'] != data['year']):
            prevData['energy_consumption_year_ht'] = 0.000
            prevData['energy_consumption_year_nt'] = 0.000
            prevData['energy_consumption_year'] = 0.000
            prevData['energy_production_year_t1'] = 0.000
            prevData['energy_production_year_t2'] = 0.000
            prevData['energy_production_year'] = 0.000

        data['status'] = 'READY'

    else:
        if(LOGGERON):
            logger.info(f"init previous data")
        prevData = data

    # serial port read data d2 protocoll for ISk5MT174
    # /ISk5MT174-0001               Device Name
    # 0.9.1(120054)                 Current time (hh:mm:ss)
    # 0.9.2(1200703)                Date (1YY.MM.DD)
    # 0.0.0(00339188)               *Device address 1
    # 0.2.0(1.03)                   *Firmware version
    # C.1.6(FDF5)                   *Firmware check sum
    # 1.8.1(0011404.409*kWh)        Positive active energy (A+) in tariff T1 [kWh]
    # 1.8.2(0023813.725*kWh)        Positive active energy (A+) in tariff T2 [kWh]
    # 2.8.1(0015608.962*kWh)        Negative active energy (A-) in tariff T1 [kWh]
    # 2.8.2(0000900.569*kWh)        Negative active energy (A-) in tariff T2 [kWh]

    if(LOGGERON):
        logger.info(f"start reading data")

    try:
        while True:

            response = ser.readline()
            strData = response.decode()

            # timestamp form mt174 protocol
            if (re.findall("0.9.1", strData)):
                t = strData.split('(', 1)[1].split(')')[0]
                data['time'] = t[0:2] + ':' + t[2:4] + ':' + t[4:6]

            # date from mt174 protocol
            if (re.findall("0.9.2", strData)):
                t = strData.split('(', 1)[1].split(')')[0]
                data['date'] = t[1:3] + '-' + t[3:5] + '-' + t[5:7]

            # decode consumption and production data
            if(strData.startswith('1.8.') or strData.startswith('2.8.')):

                strTariff = strData[0:5]
                val = strData.split('(', 1)[1].split(')')[0]
                val_rem = val.replace('*kWh', '')

                # all meter data for consumption and production
                if(strTariff == '1.8.1'):
                    data['energy_consumption_meter_ht'] = float(val_rem)
                if(strTariff == '1.8.2'):
                    data['energy_consumption_meter_nt'] = float(val_rem)
                if(strTariff == '2.8.1'):
                    data['energy_production_meter_t1'] = float(val_rem)
                if(strTariff == '2.8.2'):
                    data['energy_production_meter_t2'] = float(val_rem)

                numberOfLine = numberOfLine + 1

            # prepare the data for consumption and production
            if(numberOfLine == 4):

                # current consumption
                data['energy_consumption_ht'] = round(data['energy_consumption_meter_ht'] - prevData['energy_consumption_meter_ht'], 3)
                data['energy_consumption_nt'] = round(data['energy_consumption_meter_nt'] - prevData['energy_consumption_meter_nt'], 3)
                data['energy_consumption'] = round(data['energy_consumption_ht'] + data['energy_consumption_nt'], 3)

                # current production
                data['energy_production_t1'] = round(data['energy_production_meter_t1'] - prevData['energy_production_meter_t1'], 3)
                data['energy_production_t2'] = round(data['energy_production_meter_t2'] - prevData['energy_production_meter_t2'], 3)
                data['energy_production'] = round(data['energy_production_t1'] + data['energy_production_t2'], 3)

                # consumption per day
                data['energy_consumption_day_ht'] = round(prevData['energy_consumption_day_ht'] + data['energy_consumption_ht'], 3)
                data['energy_consumption_day_nt'] = round(prevData['energy_consumption_day_nt'] + data['energy_consumption_nt'], 3)
                data['energy_consumption_day'] = round(data['energy_consumption_day_ht'] + data['energy_consumption_day_nt'], 3)

                # production per day
                data['energy_production_day_t1'] = round(prevData['energy_production_day_t1'] + data['energy_production_t1'], 3)
                data['energy_production_day_t2'] = round(prevData['energy_production_day_t2'] + data['energy_production_t2'], 3)
                data['energy_production_day'] = round(data['energy_production_day_t1'] + data['energy_production_day_t2'], 3)

                # consumption per month
                data['energy_consumption_month_ht'] = round(prevData['energy_consumption_month_ht'] + data['energy_consumption_ht'], 3)
                data['energy_consumption_month_nt'] = round(prevData['energy_consumption_month_nt'] + data['energy_consumption_nt'], 3)
                data['energy_consumption_month'] = round(data['energy_consumption_month_ht'] + data['energy_consumption_month_nt'], 3)

                # production per month
                data['energy_production_month_t1'] = round(prevData['energy_production_month_t1'] + data['energy_production_t1'], 3)
                data['energy_production_month_t2'] = round(prevData['energy_production_month_t2'] + data['energy_production_t2'], 3)
                data['energy_production_month'] = round(data['energy_production_month_t1'] + data['energy_production_month_t2'], 3)

                # consumption per year
                data['energy_consumption_year_ht'] = round(prevData['energy_consumption_year_ht'] + data['energy_consumption_ht'], 3)
                data['energy_consumption_year_nt'] = round(prevData['energy_consumption_year_nt'] + data['energy_consumption_nt'], 3)
                data['energy_consumption_year'] = round(data['energy_consumption_year_ht'] + data['energy_consumption_year_nt'], 3)

                # production per yeaf
                data['energy_production_year_t1'] = round(prevData['energy_production_year_t1'] + data['energy_production_t1'], 3)
                data['energy_production_year_t2'] = round(prevData['energy_production_year_t2'] + data['energy_production_t2'], 3)
                data['energy_production_year'] = round(data['energy_production_year_t1'] + data['energy_production_year_t2'], 3)

                # total consumption and production
                data['energy_consumption_total'] = round(data['energy_consumption_meter_ht'] + data['energy_consumption_meter_nt'], 3)
                data['energy_production_total'] = round(data['energy_production_meter_t1'] + data['energy_production_meter_t2'], 3)
                data['energy_total'] = round(data['energy_consumption_total'] - data['energy_production_total'], 3)

                # system timestamp
                dateTimeObj = datetime.now()
                data['timestamp'] = dateTimeObj.strftime('%Y-%m-%d %H:%M:%S.%f')
                data['periode'] = dateTimeObj.strftime('%Y-%m-%d')

                # save and publish the current values
                json_data = json.dumps(data)
                with open(DATAFILE, "w") as f:
                    f.write(json_data)

                # publish the data to the mqtt brocker
                publish.single(MQTTTOPIC,
                               payload=json_data, qos=0, retain=False,
                               hostname=MQTTHOST, port=MQTTPORT,
                               client_id=MQTTCLIENT, keepalive=60, auth=MQTTAUTH)

                if(LOGGERON):
                    logger.info(
                        f"Published data to {MQTTHOST} from {data['deviceid']}: {json_data}")

                # build the result output to the console
                csv_formatter = ArgsToCsv()
                output = csv_formatter.stringify(
                    data['date'],
                    data['time'],
                    data['energy_consumption_meter_ht'],
                    data['energy_consumption_meter_nt'],
                    data['energy_production_meter_t1'],
                    data['energy_production_meter_t2'],
                    data['energy_consumption_day'],
                    data['energy_consumption_month'],
                    data['energy_consumption_year'],
                    data['energy_production_day'],
                    data['energy_production_month'],
                    data['energy_production_year'],
                    data['timestamp']
                )

                header = "date,time,ht,nt,t1,t2,total_day,total_month,total_year,prod_day,prod_month,prod_year,timestamp"
                __savedata__(header, output)

                ## print(output, end="")

                break

    except Exception:
        # We catch everything and publish the error message
        logger.exception("Error")

    ser.close()

    if(LOGGERON):
        logger.info('End reading data')

main()
Bascht74 commented 3 years ago

@zibous Thx, but to me it doesn't look so simple... :-) But it shows the "write" command to get the data... I will look into it, but it would be great to have a simpler resolution for other people here in Germany & Austria. They don't have the P1 interface so they need to send that command...

zibous commented 3 years ago

@Bascht74

This is the testcase for the serial interface.

https://www.amazon.de/Weidmann-Elektronik-Stromz%C3%A4hler-Infrarot-Lesekopf/dp/B01B8N0ASY

Bildschirmfoto 2021-05-30 um 19 26 30

#!/usr/bin/env python3
import serial
import time

## ls -l /dev/serial/{by-path,by-id}/*
## lrwxrwxrwx 1 root root 13 Jul  2 13:33 /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_D307N80T-if00-port0 -> ../../ttyUSB0
## lrwxrwxrwx 1 root root 13 Jul  2 13:33 /dev/serial/by-path/pci-0000:00:14.0-usb-0:5:1.0-port0 -> ../../ttyUSB0

## dmesg | grep tty
## [534356.058130] usb 2-5: FTDI USB Serial Device converter now attached to ttyUSB0

SERIALPORT = "/dev/ttyUSB0"
BAUDRATE = 300

ser = serial.Serial(SERIALPORT, BAUDRATE, serial.SEVENBITS, serial.PARITY_EVEN)
ser.write(b"\x2F\x3F\x21\x0D\x0A")

time.sleep(0.5)
numberOfLine = 0

while True:
  response = ser.readline()
  print(response)
  numberOfLine = numberOfLine + 1
  if (numberOfLine >= 11):
     break
ser.close()
Bascht74 commented 3 years ago

Hi @zibous , I had to tweak it a bit, my reader needs an ACK. Without it, it won't send any data...

#!/usr/bin/env python3
import serial
import time

## ls -l /dev/serial/{by-path,by-id}/*
## lrwxrwxrwx 1 root root 13 Jul  2 13:33 /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_D307N80T-if00-port0 -> ../../ttyUSB0
## lrwxrwxrwx 1 root root 13 Jul  2 13:33 /dev/serial/by-path/pci-0000:00:14.0-usb-0:5:1.0-port0 -> ../../ttyUSB0

## dmesg | grep tty
## [534356.058130] usb 2-5: FTDI USB Serial Device converter now attached to ttyUSB0

SERIALPORT = "/dev/ttyUSB0"
BAUDRATE      = 300

ser = serial.Serial(SERIALPORT, BAUDRATE, serial.SEVENBITS, serial.PARITY_EVEN)
ser.write(b"\x2F\x3F\x21\x0D\x0A")

time.sleep(0.2)

response = ser.readline() # read header
print(response)

time.sleep(0.2)

ser.write(b"\x06\x30\x30\x30\x0D\x0A")   # send ACK, send the data with 300 Baud (<ACK>000)

numberOfLine = 0

while True:
  response = ser.readline()
  print(response)
  numberOfLine = numberOfLine + 1
  if (numberOfLine >= 26):
     break
ser.close()

Result:

python_scripts python3 serial-test.py
b'/ISK5\\2MT382-1008\r\n'
b'\x020-0:96.1.1(3149534B30303733313039383037)\r\n'
b'0-0:128.20.0(013)\r\n'
b'1-0:0.9.2(210530)\r\n'
b'1-0:0.9.1(221439)\r\n'
b'1-0:1.7.0(001.342*kW)\r\n'
b'1-0:2.7.0(000.000*kW)\r\n'
b'1-0:1.8.0(010227.59*kWh)\r\n'
b'1-0:1.8.1(006123.39*kWh)\r\n'
b'1-0:1.8.2(001413.79*kWh)\r\n'
b'1-0:1.8.3(000982.76*kWh)\r\n'
b'1-0:1.8.4(001707.64*kWh)\r\n'
b'1-0:1.8.5(000000.00*kWh)\r\n'
b'1-0:1.8.6(000000.00*kWh)\r\n'
b'1-0:1.8.7(000000.00*kWh)\r\n'
b'1-0:1.8.8(000000.00*kWh)\r\n'
b'1-0:1.2.0(106.732*kW)\r\n'
b'1-0:1.6.0(007.072*kW)\r\n'
b'1-0:2.8.0(006765.31*kWh)\r\n'
b'1-0:3.8.0(000122.31*kvarh)\r\n'
b'1-0:4.8.0(006279.24*kvarh)\r\n'
b'1-0:0.2.2(Smart001)\r\n'
b'1-0:0.3.0(00500)\r\n'
b'1-0:0.3.1(00500)\r\n'
b'1-0:0.3.3(250)\r\n'
b'0-0:97.97.0(00000000)\r\n'
b'!\r\n'
➜  python_scripts
zibous commented 3 years ago

@Bascht74 Looks good, now you can decode the telegram and send this as MQTT Message... 👍

mtdcr commented 3 years ago

@Bascht74 The D0 protocol is out of scope for pysml.

You could create a separate library, though (based on serial_asyncio), which we then could utilize to support your energy meter with the edl21 component of Home Assistant.

Bascht74 commented 3 years ago

@mtdcr I asked my power provider this week if they can set up my P1-Port so that I can use the official "dutch" integration (that is not able to . They said yes! I will try. If that is not working, I will try to use Tasmota to "trigger" the reading and send it via IP to that integration. Next weekend I will know more.

Bascht74 commented 3 years ago

This worked out of the box! The P1-Port pulls all data so that you can use the DSMR integration (https://www.home-assistant.io/integrations/dsmr/). The not so easy part was to convince the power company to connect the cable because you cannot do this yourself...

But thx anyway for the offer!

Sebastian