Die serielle mag einfach keine Baudratenumstellung innerhalb der Kommunikation, das ist mir weder mit Python noch mit Perl gelungen ... tausend mal die man-pages gelesen und verstanden ... erst deswegen bin ich wieder beim socat gelandet. In der Kommunikation gibt es da ja keine riesigen Unterschiede zwischen perl und Python was die serielle angeht -> open/recv/close.
Das Plugin stand hier such schon komplett auf serieller Ebene, aber ich hab einfach keine Lust 120! Sekunden auf einen Datensatz meines WP_Zählers zu warten weil der nur mit 300baud dröselt und aus 120 Zeilen genau eine gebraucht wird.
Der socat ist schon nett für die serielle im LAN, ansonsten macht er das Leben auch nicht einfacher, das bisschen "\n" und ”\r” was man meist sucht findet man auch leichter/verständlicher.
Grüße
Ankündigung
Einklappen
Keine Ankündigung bisher.
Smartmeter Plugin - Tester gesucht
Einklappen
X
-
Hallo Mirko,
wenn Du mal wieder Zeit und Lust hast, kannst Du Dir ja mal das DMX-Plugin ansehen, dort wird auch über eine serielle Schnittstelle kommuniziert.
Das ist ziemlich direkt und stabil.
Ich verstehe auch nicht wieso das WG-Lager socat so verehrt.
Bis bald
Marcus
Einen Kommentar schreiben:
-
Hallo Marcus,
Danke nochmal für Deine Tips, haben mir gut geholfen das ganze zu verstehen.
Die Doku für das Plugin mache ich noch fertig, ansonsten nervt mich hier gerade wieder das ganze serielle/socat Gefrickel mit den unzuverlässigen eHZ.
Ich persönlich bleib da bei meinen meinen zwei perl/cronjob-scripten die seit über einem Jahr fehlerfrei laufen. Vielleicht will sich jemand anders ja dessen noch mal annehmen - ich kann/will da im Moment keine weitere Zeit versenken.
Grüße
Einen Kommentar schreiben:
-
Hallo Mirko,
+1
vllt. gefällt Dir die Lösung aus dem mpd-Plugin: https://github.com/mknx/smarthome/bl..._init__.py#L79Zitat von JuMi2006 Beitrag anzeigenMomentan fehlt mir noch die Idee wie ich bei mehreren Zählern/Ausleseköpfen die items der passenden Zähler-id zuordne und dass noch mit dem sheduler kombiniere.
Doku folgt noch...
Bis bald
Marcus
Einen Kommentar schreiben:
-
Smartmeter Plugin - Tester gesucht
Ich hab für DLMS/D0/IEC-62056 Zähler ein Plugin fertig gemacht.
Das Plugin kann in der Kommandozeile aufgerufen werden und zeigt dann die verfügbaren Werte des Zählers an:
Mitzugeben sind die Parameter device und baudrate.Code:python /usr/local/smarthome/plugins/dlms/__init__.py [COLOR="SeaGreen"]/dev/ttyUSB0[/COLOR] [COLOR="Red"]auto[/COLOR]
Die Baudrate wird mit "auto" automatisch anhand der Zählerkennung ermittelt, kann aber auch mit "300" auf z.B. 300baud verbleiben.
Ergebnis:
Hier sehen wir jetzt die Obis-Kennzahlen und deren Werte. Die braucht man für das anlegen der items.Code:reading from device: /dev/ttyUSB0 baudrate: auto /LGZ4ZMF100AC.M23 [B]1.8.1[/B] 004442.544 kWh 1.8.2 000000.000 kWh 1.8.3 000000.000 kWh 1.8.4 000000.000 kWh 1.8.0 004442.544 kWh 2.8.0 000000.000 kWh 15.8.0 004442.544 kWh 32.7 240 V 52.7 240 V 72.7 241 V [B]31.7[/B] 003.36 A 51.7 001.11 A [B]71.7[/B] 001.85 A 16.7 000.78 kW Reading takes: 4.73436403275
items.conf
Das Plugin liegt unter /plugins/dlms/__init__.pyCode:[zaehler] [[stand]] type = num dlms_code = "[B]1.8.1[/B]" [[strom_l1]] type = num dlms_code = "[B]31.7[/B]" [[strom_l3]] type = num dlms_code = "[B]71.7[/B]"
plugin.conf:Hier also alle 60 Sekunden die Werte für Zählerstand absolut und Stromstärke L1 & L3.Code:[smartmeter] class_name = DLMS class_path = plugins.dlms serialport = "/dev/ttyUSB0" baudrate = auto cycle = 60
ToDo:
- Unterstützung von mehreren Zählern/seriellen Ports im Plugin
- Unterstützung von mehreren Zählern über einen IR-Auslesekopf
Momentan fehlt mir noch die Idee wie ich bei mehreren Zählern/Ausleseköpfen die items der passenden Zähler-id zuordne und dass noch mit dem sheduler kombiniere.
Grüße
../plugins/dlms/__init__.py:
PHP-Code:#!/usr/local/bin/python
import os
import sys
import logging
import struct
import time
logger = logging.getLogger('')
class DLMS():
def __init__(self, smarthome, serialport, baudrate, cycle):
self._sh = smarthome
self.port = serialport
self.obis = {}
self.baud = baudrate
self.cycle = int(cycle)
def run(self):
self.alive = True
self._sh.scheduler.add('DLMS', self.connect, cycle=self.cycle)
logger.debug("Cycle: {0}".format(self.cycle))
global from_shell
from_shell = False
self.connect()
def stop(self):
self.alive = False
def connect(self):
logger.debug("DLMS: connect")
request = "\x2f\x3f\x21\x0d\x0a"
cmd = "echo '"+request+"' | socat -T 1 - "+self.port+",raw,echo=0,b300,parenb=1,parodd=0,cs7,cstopb=0"
try:
self.id = os.popen(cmd).readline()
except:
if from_shell:
print("[connect] error main: {0}".format(e))
else:
logger.warning("DLMS: {0}".format(e))
if len(self.id) > 0:
if from_shell:
print self.id.strip()
self.ack(self.id)
else:
self.ack(self.id)
def ack(self,id):
speedrates = ['300','600','1200','2400','4800','9600']
acks = ["\x06\x30\x30\x30\x0d\x0a","\x06\x30\x31\x30\x0d\x0a","\x06\x30\x32\x30\x0d\x0a","\x06\x30\x33\x30\x0d\x0a","\x06\x30\x34\x30\x0d\x0a","\x06\x30\x35\x30\x0d\x0a"]
arr = [[0,"300","\x06\x30\x30\x30\x0d\x0a"],[1,"600","\x06\x30\x31\x30\x0d\x0a"],[2,"1200","\x06\x30\x32\x30\x0d\x0a"],[3,"2400","\x06\x30\x33\x30\x0d\x0a"],[4,"4800","\x06\x30\x34\x30\x0d\x0a"],[5,"9600","\x06\x30\x35\x30\x0d\x0a"]]
if self.baud == "auto":
speed = speedrates[int(self.id[4])]
ack = acks[int(self.id[4])]
else:
for n in arr:
if n[1] == self.baud:
speed = n[1]
ack = n[2]
try:
cmd = "echo '"+ack+"' | socat -T 1 - "+self.port+",raw,echo=0,b300,parenb=1,parodd=0,cs7,cstopb=0; socat -T 1 - "+self.port+",raw,echo=0,b"+speed+",parenb=1,parodd=0,cs7,cstopb=0"
lines = os.popen(cmd).readlines()
except:
print("[ack] error main: {0}".format(e))
self.parse(lines)
def parse(self,lines):
for line in lines[:-2]:
if "*" in line:
i = line[:-2].split("(")
j = i[1].split("*")
k = j[1].split(")")
c_obis = i[0]
c_value = float(j[0])
c_unit = k[0]
if not from_shell:
self.refresh(c_obis,c_value,c_unit)
else:
print i[0] + " " + j[0] + " " + k[0]
def refresh(self,c_obis,c_value,c_unit):
for codes in self.obis:
if codes == c_obis:
self.obis[codes](c_value,'DLMS', 'refresh')
#logger.debug("DLMS: Parameter/Refresh: {0}".format(codes))
def parse_item(self, item):
if 'dlms_code' in item.conf:
logger.debug("parse item: {0}".format(item))
obis_code = item.conf['dlms_code']
self.obis[obis_code] = item
logger.debug("DLMS: new set = item:{0} obis:{1}".format(item,obis_code))
def main():
try:
start = time.time()
#serialport = '/dev/usbserial-alcdut1'
#serialport = '/dev/usbserial-A600eZF1'
if sys.argv[1]:
serialport = sys.argv[1]
print ("reading from device: {0}".format(serialport))
if sys.argv[2]:
baudrate = sys.argv[2]
print ("baudrate: {0}".format(baudrate))
smarthome = ''
cycle = 60
global from_shell
from_shell = True
meter = DLMS(smarthome,serialport,baudrate,cycle)
meter.connect()
cycletime = time.time() - start
print ("Reading takes: {0}".format(cycletime))
except Exception, e:
print "[main]: {0}".format(e)
print "usage: __init__.py <serial_port> <baudrate>"
print "baudrate: auto,300,600,1200,2400,4800,9600"
return 1
if __name__ == "__main__":
sys.exit(main())
Stichworte: -


Einen Kommentar schreiben: