Hallo,
ich habe ein Plugin zum interpretieren von EMH Stromzählerdaten geschrieben. Die Daten werden über die serielle Schnittstelle gelesen.
Wird dieses Plugin genutzt kann ich zu 30% smarthome.py aus dem interaktiven Mode nicht mit CTRL+C beenden. Die Shell des interaktiven Modes friert dann ein und die sh Instanz läuft fröhlich weiter. Auch ein kill <PID> hilft dann nicht. Erst ein kill -9 <PID> beendet die Instanz. Dann wird die Shell in der der interactive Modus gelaufen ist auch wieder frei. Manchmal sind danach die Terminaleinstellungen verstellt (kein Keyboard echo mehr, keine Newline bei enter etc).
Ganz selten bekomme ich diese Fehlermeldung:
Aufgrund dieser Probleme gibt es dieses Plugin auch noch nicht im GIT - da ich selbst im Dev Branach nichts veröffentlichen möchte was die Stabilität des ganzen Systems beeinträchtigen könnte.
Anbei der Quellcode von dem Plugin:
Wäre super wenn jemand eine Idee hätte wie das beenden sauber umgesetzt werden kann.
LG
Mode
ich habe ein Plugin zum interpretieren von EMH Stromzählerdaten geschrieben. Die Daten werden über die serielle Schnittstelle gelesen.
Wird dieses Plugin genutzt kann ich zu 30% smarthome.py aus dem interaktiven Mode nicht mit CTRL+C beenden. Die Shell des interaktiven Modes friert dann ein und die sh Instanz läuft fröhlich weiter. Auch ein kill <PID> hilft dann nicht. Erst ein kill -9 <PID> beendet die Instanz. Dann wird die Shell in der der interactive Modus gelaufen ist auch wieder frei. Manchmal sind danach die Terminaleinstellungen verstellt (kein Keyboard echo mehr, keine Newline bei enter etc).
Ganz selten bekomme ich diese Fehlermeldung:
Code:
>>> Unhandled exception in thread started by <bound method Plugin._bootstrap of <Plugin(ehz, stopped 139903929153280)>>
Anbei der Quellcode von dem Plugin:
Code:
#!/usr/bin/env python3
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
#########################################################################
# Copyright 2013 KNX-User-Forum e.V. https://knx-user-forum.de/
#########################################################################
# This file is part of SmartHome.py. http://mknx.github.io/smarthome/
#
# SmartHome.py is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SmartHome.py is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
#########################################################################
import logging
import serial
import struct
logger = logging.getLogger('ehz_emh')
class ehz_emh():
def __init__(self, smarthome, port = '/dev/ttyUSB0', baudrate = 9600, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS, \
timeout = .1, length_emh_frame = 392, length_tarif_data = 23, length_tarif_value = 5):
self._sh = smarthome
self._tarif = {}
self._port = port
self._baudrate = baudrate
self._parity = parity
self._stopbits = stopbits
self._bytesize = bytesize
self._timeout = timeout
self._length_emh_frame = length_emh_frame
# self._rx_buffer = rx_buffer
self._length_tarif_data = length_tarif_data
self._length_tarif_value = length_tarif_value
self._ser = serial.Serial(port = self._port, baudrate = self._baudrate, parity = self._parity, stopbits = self._stopbits, \
bytesize = self._bytesize, timeout = self._timeout)
if self._ser.isOpen():
logger.info("eHZ_EMH is connected to {0}".format(self._ser.portstr))
else:
logger.error("Can not connect to {0}".format(self._ser.portstr))
def run(self):
self.alive = True
self._ser.flushInput()
while self.alive:
if not self._ser.isOpen():
self._ser = serial.Serial(port = self._port, baudrate = self._baudrate, parity = self._parity, stopbits = self._stopbits, \
bytesize = self._bytesize, timeout = self._timeout)
logger.info("Reconnect needed, now connected to {0}".format(self._ser.portstr))
dataset = self._ser.read(self._length_emh_frame)
if len(dataset) == 0:
# logger.debug("No eHZ Data received from port ".format(self._ser.portstr))
continue
if len(dataset) != self._length_emh_frame:
logger.warning("Invalid eHZ Data. Len received: {0} Len expected: {1}".format(len(dataset), self._length_emh_frame))
self._ser.flushInput()
continue
# print (type(dataset))
# print (dataset)
# for line in dataset:
# print("{0:02X}".format(line), end = " ")
# print("")
crc = self._sml_crc16(dataset)
if [dataset[-2],dataset[-1]] == [crc[-2],crc[-1]]:
pass
# logger.debug("CRC OK")
else:
logger.warning("CRC NOK")
continue
for tarif in self._tarif:
value = self._get_tarif(dataset, tarif)
self._tarif[tarif](value)
def stop(self):
self.alive = False
self._ser.close()
def parse_item(self, item):
if 'ehz_emh_tarif' in item.conf:
tarif = item.conf['ehz_emh_tarif']
logger.debug("parse item: {0}".format(item))
if tarif not in self._tarif:
self._tarif[tarif] = item
else:
self._tarif[tarif].append(item)
return None
def parse_logic(self, logic):
pass
def update_item(self, item, caller=None, source=None, dest=None):
if caller != 'plugin':
logger.info("update item: {0}".format(item.id()))
# CRC Berechnung mit Lookuptable
def _sml_crc16(self, cp):
crctab = [0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, \
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, \
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, \
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, \
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, \
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, \
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, \
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, \
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, \
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, \
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, \
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, \
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, \
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, \
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, \
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78]
fcs = 0xFFFF
index = 0
length = len(cp) - 2
while (length):
length -= 1
fcs = (fcs >> 8) ^ crctab[(fcs ^ cp[index]) & 0xff]
index += 1
fcs ^= 0xffff
return struct.pack('H',fcs)
def _get_tarif(self, dataset, tarif):
result1 = None
result2 = None
# Auf Anfangssequenz von ZählerstandBlock <tarif> parsen und diesen Block in result1 vorhalten
search = bytearray.fromhex('77 07 01 00')
for n in tarif:
search.append(int(n))
search.append(0xFF)
cnt = dataset.find(search)
if cnt >= 0:
result1 = dataset[cnt:cnt+self._length_tarif_data]
if result1 is None:
logger.info("Tarif dataset {0} not Found!".format(tarif))
return
# Auf Anfangssequenz von Zäherstand parsen und diesen in result2 vorhalten
search = bytearray.fromhex('62 1E 52 FF 56')
length = len(search)
cnt = result1.find(search)
if cnt >= 0:
result2 = result1[cnt+length:cnt+length+self._length_tarif_value]
if result2 is None:
logger.info("Tarif value {0} not Found!".format(tarif))
return
# Zäherstand konvertieren hex->int
num = 0
for data in result2:
num <<= 8
num |= data
return num / 10000
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
myplugin = Plugin('ehz_emh')
myplugin.run()
LG
Mode


Kommentar