home-assistant / core

:house_with_garden: Open source home automation that puts local control and privacy first.
https://www.home-assistant.io
Apache License 2.0
73.92k stars 30.98k forks source link

Home Assistant 0.112.0 - Integration platform: edl21 does not supply sensor data. #37349

Closed zibous closed 3 years ago

zibous commented 4 years ago

Integration platform: edl21 does not supply sensor data or Integration platform: edl21 not loaded.

Environment

Systemzustand
arch    x86_64
dev false
docker  false
hassio  false
installation_type   Home Assistant Core
os_name Linux
os_version  5.4.0-39-generic
python_version  3.8.2
timezone    Europe/Vaduz
version 0.112.0
virtualenv  true
Lovelace
dashboards  2
mode    yaml
resources   23
Frontend-Version: 20200701.0 - latest

Problem-relevant configuration.yaml

sensor:
  - platform: edl21
    name: Stromzähler
    serial_port: /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_D307N80T-if00-port0

logger:
  default: critical
  logs:
    homeassistant.components.system_log.external: info
    homeassistant.components.media_player: info
    homeassistant.components.edl21.sensor: debug

Traceback/Error logs

None

Additional information

When I query the interface with the following script, I get 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("\x2F\x3F\x21\x0D\x0A")
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()

Result:

 b'ISk5MT174-0001\r\n'
 b'\x020.9.1(150802)\r\n'
 b'0.9.2(1200702)\r\n'
 b'0.0.0(00339188)\r\n'
 b'0.2.0(1.03)\r\n'
 b'C.1.6(FDF5)\r\n'
 b'1.8.1(0011400.587*kWh)\r\n'
 b'1.8.2(0023806.592*kWh)\r\n'
 b'2.8.1(0015608.024*kWh)\r\n'
 b'2.8.2(0000900.569*kWh)\r\n'
 b'F.F.0(0000000)\r\n'

Can it be that I am doing something wrong with the configuration?

probot-home-assistant[bot] commented 4 years ago

Hey there @mtdcr, mind taking a look at this issue as its been labeled with an integration (edl21) you are listed as a codeowner for? Thanks! (message by CodeOwnersMention)

mtdcr commented 4 years ago

The edl21 component currently supports SML-based meters only. Your meter speaks D0. It would certainly be possible to add support for this protocol, but I have no way to test it. Maybe this library could help: https://github.com/pwitab/iec62056-21. Do you want to give it a try? It doesn't use asyncio, though.

zibous commented 4 years ago

Yes, that's right, the Sk5MT174 electricity meter only seems to deliver the IEC 62056-21 (D0) protocol. With iec62056-21 I did not get any further, it does not work.

An alternative would be to add DO to the edl21 component code.


sensor:
  - platform: edl21
    name: Stromzähler
    serial_port: /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_D307N80T-if00-port0
    protocol: SML

  ## for IEC 62056-21 protocoll
  - platform: edl21
    name: Stromzähler ISk5MT174
    serial_port: /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_D307N80T-if00-port0
    protocol: D0
mtdcr commented 4 years ago

I think it may be worth getting https://github.com/pwitab/iec62056-21 to work with your meter. Otherwise we'd need to find another library or create our own. If we have something that works well from the command-line, we can integrate it into the edl21 component.

I also found https://github.com/jonkerj/iec62056 (a parser library without serial I/O), which wasn't able to decode your telegram. However, your telegram doesn't seem to be complete (missing leading / and trailing !\r\n\x03 + checksum characters), according to https://wiki.volkszaehler.org/hardware/channels/meters/power/edl-ehz/iskraemeco_mt174 (German).

N.b.: The dsmr component seems to handle very similar telegrams. Maybe it would be worth looking into it as an alternative.

zibous commented 4 years ago

@mtdcr Thanks Andreas,

I also found https://github.com/jonkerj/iec62056 (a parser library without serial I/O)

This library looks promising. I also looked at DSMR, it is similar to the processing of the protocol.

In the meantime, I've created a simple script (only for MT174) (I've never programmed in Python before) - probably not pretty, but it does give results.

Script see: https://pastebin.com/v5BMGiLu

Results:

Bildschirmfoto 2020-07-04 um 18 17 23

{
    "device": "Stromz\u00e4hler Haus",
    "deviceid": "ISk5MT174-0001",
    "date": "20-07-04",
    "time": "00:00:90",
    "energy_consumption_meter_ht": 11406.564,
    "energy_consumption_meter_nt": 23820.595,
    "energy_production_meter_t1": 15622.627,
    "energy_production_meter_t2": 901.13,
    "energy_consumption_ht": 0.0,
    "energy_consumption_nt": 0.0,
    "energy_consumption": 0.0,
    "energy_production_t1": 0.0,
    "energy_production_t2": 0.175,
    "energy_production": 0.175,
    "energy_consumption_day_ht": 0.409,
    "energy_consumption_day_nt": 3.006,
    "energy_consumption_day": 3.415,
    "energy_production_day_t1": 7.796,
    "energy_production_day_t2": 0.561,
    "energy_production_day": 8.357,
    "energy_consumption_month_ht": 421.461,
    "energy_consumption_month_nt": 1076.322,
    "energy_consumption_month": 1497.783,
    "energy_production_month_t1": 1448.736,
    "energy_production_month_t2": 4.374,
    "energy_production_month": 1453.11,
    "energy_consumption_year_ht": 421.461,
    "energy_consumption_year_nt": 1076.322,
    "energy_consumption_year": 1497.783,
    "energy_production_year_t1": 1448.736,
    "energy_production_year_t2": 4.374,
    "energy_production_year": 1453.11,
    "energy_consumption_total": 35227.159,
    "energy_production_total": 16523.757,
    "energy_total": 18703.402,
    "unit_of_measurement": "kWh",
    "icon": "mdi:billboard",
    "timestamp": "2020-07-04 13:15:10.550744",
    "periode": "2020-07-04",
    "month": "2020-07",
    "year": "2020",
    "version": "1.0.0",
    "status": "READY"
}

I just run the script via a cronjob every 5 minutes and get the data via the MQTT Brocker for the home assistant

nianj commented 4 years ago

Hi there, I'm trying to integrate my ISKRA MT681 via the Weidmann Elektronik Schreib-/Lesekopf USB too.

Via the script from @zibous and a change in baudrate to 9600 I get the following output: removed

But I couldn't get any readable output via the libraries https://github.com/pwitab/iec62056-21 or https://github.com/jonkerj/iec62056. I also tried the dsmr integration in home assistant but no luck either. The receiver works with the emlog test tool (https://shop.weidmann-elektronik.de/index.php?page=product&info=&info=141&dl_media=164) on windows.

zibous commented 4 years ago

@nianj

You can try this:

ESP-12F https://github.com/Necromancer1982/ESPReadEHZ python https://github.com/Moneybox76/HomeAssistant_SML_ISKRA_MT681

mtdcr commented 4 years ago

@nianj: Your log file seems to be truncated. The script posted above is not very useful for SML (which is a binary protocol), because it expects ASCII input.

Please create a new binary log file, for example with timeout 10 socat stdio /dev/ttyUSB0 >mt681.bin. When you examine the file afterwards, e.g. with hd mt681.bin, you should find multiple occurences of 1b 1b 1b 1b.

Note that such logs contain unique identifiers. If you'd like to avoid posting them in public, you can send them via mail: obi at saftware dot de (and delete your partial log above).

github-actions[bot] commented 3 years ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

Whitenoises commented 3 years ago

@zibous: Hello Peter, Could you find a solution for the MT174? It's the last component missing for me in order ro have all energy conumption monitored in my home. Previously I had an IoBroker installation and there the monitoring was no problem with the smat meter plug-in.

zibous commented 3 years ago

@Whitenoises Hi,

I use a simple python script to get the data from the MT174.

#!/usr/bin/python3

# 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

# SERIALPORT = '/dev/ttyUSB0'
# BAUDRATE = 300
# TIMEOUT = 10
# MQTTAUTH = {'username': 'theAdmin', 'password': 'mysecretPassword'}
# MQTTHOST = 'mqttbrocker.local'
# 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 = True

# 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['state_class'] = 'measurement'
    data['last_reset'] = '2021-08-05T08:05:00+00:00'
    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['hostname']='mt715.service.local'
    data['attribution'] = 'Data provided by Weidmann USB IR Kopf'

    # 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()

Hardware ISKRA MT174 Three-phase multifunction electronic meter Weidmann USB IR Kopf see: https://shop.weidmann-elektronik.de/index.php?page=product&info=24

MT174_SENSOR

Whitenoises commented 3 years ago

Hallo Herr Siebler,

Besten Dank für Ihre Antwort. Leider bin ich noch ein Novice in Python aber ich hab es geschaft meinen Zählerstand mittels Node Red auszulesen (1.8.1 & 1.8.2). Jetzt muss ich es nur schaffen, die Ergebnisse auf das Home Assistant Dashboard zu spielen.

Schöne Grüsse und besten Dank, Georg

Am Do., 14. Okt. 2021 um 20:14 Uhr schrieb Peter Siebler < @.***>:

@Whitenoises https://github.com/Whitenoises Hi,

I use a simple python script to get the data from the MT174.

!/usr/bin/python3

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

SERIALPORT = '/dev/ttyUSB0'

BAUDRATE = 300

TIMEOUT = 10

MQTTAUTH = {'username': 'theAdmin', 'password': 'mysecretPassword'}

MQTTHOST = 'mqttbrocker.local'

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 = True

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['state_class'] = 'measurement'

data['last_reset'] = '2021-08-05T08:05:00+00:00'

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['hostname']='mt715.service.local'

data['attribution'] = 'Data provided by Weidmann USB IR Kopf'

# 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()

Hardware ISKRA MT174 Three-phase multifunction electronic meter Weidmann USB IR Kopf see: https://shop.weidmann-elektronik.de/index.php?page=product&info=24

[image: MT174_SENSOR] https://user-images.githubusercontent.com/30198737/137372764-b5db2ba9-900a-4d24-a75a-287e8e483199.png

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/home-assistant/core/issues/37349#issuecomment-943602530, or unsubscribe https://github.com/notifications/unsubscribe-auth/AV3CWCQ2IHKU3KFJPOPYDXDUG4MZVANCNFSM4OO47TEQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.