Wenn dies dein erster Besuch hier ist, lies bitte zuerst die Hilfe - Häufig gestellte Fragen durch. Du musst dich vermutlich registrieren, bevor du Beiträge verfassen kannst. Klicke oben auf 'Registrieren', um den Registrierungsprozess zu starten. Du kannst auch jetzt schon Beiträge lesen. Suche dir einfach das Forum aus, das dich am meisten interessiert.
bei mir läufts mit der fixen einstellung auf 300 nun stabil.
Nur damit es nicht durcheinander geht: "Gehen" tut das dlms-Plugin was bei sh.py nun dabei ist oder eine Version hier aus dem Thread?
Falls die sh.py-Version: Mit Checksumme?
Für mein Verständnis: Wofür brauchtest du die "float(data[0])"? Eigentlich hängen da doch per se "Num-Items" dran und das wird dann korrekt gewandelt? Ansonsten bau ich den Cast noch ein...
versuch mir doch bitte erst mal zu erklären, wo du die Unterschiede erkannt hast. Aus der Schleife werde ich nicht schlau.
Bis nach Init/Umschalten/"der Schleife" sind wir uns schon mal einig, oder?
Jetzt sendest du aber den Request scheinbar jedesmal mit 300 Baud? und schaltest dann um auf 4800?
Bei mir läuft nach dem einmaligen Umschalten alles auf 4800 - das geht bei dir nicht?
Wenn du das umbauen willst - ich habe hier ne fertige "zerlegte" Version, wo das Init mit Baudratenerkennung <-> Update/"Schleife" getrennt sind. Da müsste dann nur noch die Baudrate mit einem Einzeiler gewechselt werden...
Nur damit es nicht durcheinander geht: "Gehen" tut das dlms-Plugin was bei sh.py nun dabei ist oder eine Version hier aus dem Thread?
Falls die sh.py-Version: Mit Checksumme?
Für mein Verständnis: Wofür brauchtest du die "float(data[0])"? Eigentlich hängen da doch per se "Num-Items" dran und das wird dann korrekt gewandelt? Ansonsten bau ich den Cast noch ein...
bei mir läuft dieses hier aktuell:
Code:
#!/usr/bin/env python3
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
#########################################################################
# Copyright 2011 KNX-User-Forum e.V. https://knx-user-forum.de/
#########################################################################
# DLMS plugin for SmartHome.py. http://mknx.github.io/smarthome/
#
# This plugin 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.
#
# This plugin 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 this plugin. If not, see <http://www.gnu.org/licenses/>.
#########################################################################
import logging
import time
import serial
import re
logger = logging.getLogger('DLMS')
class DLMS():
def __init__(self, smarthome, serialport, baudrate="auto", update_cycle="60"):
self._sh = smarthome
self._update_cycle = int(update_cycle)
if (baudrate.lower() == 'auto'):
self._baudrate = -1
else:
self._baudrate = int(baudrate)
self._obis_codes = {}
self._serial = serial.Serial(
serialport, 300, bytesize=serial.SEVENBITS, parity=serial.PARITY_EVEN, timeout=2)
self._request = bytearray('\n\x06000\r\n', 'ascii')
def run(self):
self.alive = True
self._sh.scheduler.add('DLMS', self._update_values,
prio=5, cycle=self._update_cycle)
def stop(self):
self.alive = False
self._serial.close()
self._sh.scheduler.remove('DLMS')
def _update_values(self):
logger.debug("dlms: update")
start = time.time()
init_seq = bytes('\n/?!\r\n', 'ascii')
self._serial.flushInput()
self._serial.write(init_seq)
response = bytes()
prev_length = 0
try:
while self.alive:
response += self._serial.read()
length = len(response)
# break if timeout or newline-character
if (length == prev_length) or ((length > len(init_seq)) and (response[-1] == 0x0a)):
break
prev_length = length
except Exception as e:
logger.warning("dlms: {0}".format(e))
# remove echoed chars if present
if (init_seq == response[:len(init_seq)]):
response = response[len(init_seq):]
if (len(response) >= 5) and ((response[4] - 0x30) in range(6)):
if (self._baudrate == -1):
baud_capable = 300 * (1 << (response[4] - 0x30))
else:
baud_capable = self._baudrate
if baud_capable > self._serial.baudrate:
try:
logger.debug(
"dlms: meter returned capability for higher baudrate {}".format(baud_capable))
# change request to set higher baudrate
self._request[2] = response[4]
self._serial.write(self._request)
logger.debug("dlms: trying to switch baudrate")
switch_start = time.time()
# Alt1:
#self._serial.baudrate = baud_capable
# Alt2:
#settings = self._serial.getSettingsDict()
#settings['baudrate'] = baud_capable
# self._serial.applySettingsDict(settings)
# Alt3:
port = self._serial.port
self._serial.close()
del self._serial
logger.debug("dlms: socket closed - creating new one")
self._serial = serial.Serial(
port, baud_capable, bytesize=serial.SEVENBITS, parity=serial.PARITY_EVEN, timeout=2)
logger.debug(
"dlms: Switching took: {:.2f}s".format(time.time() - switch_start))
logger.debug("dlms: switch done")
except Exception as e:
logger.warning("dlms: {0}".format(e))
return
else:
self._serial.write(self._request)
response = bytes()
prev_length = 0
try:
while self.alive:
response += self._serial.read()
length = len(response)
# break if timeout or "ETX"
if (length == prev_length) or ((length >= 2) and (response[-2] == 0x03)):
break
prev_length = length
except Exception as e:
logger.warning("dlms: {0}".format(e))
logger.debug("dlms: Reading took: {:.2f}s".format(time.time() - start))
# remove echoed chars if present
if (self._request == response[:len(self._request)]):
response = response[len(self._request):]
# perform checks (start with STX, end with ETX, checksum match)
checksum = 0
# for i in response[1:]:
# checksum ^= i
# if (len(response) < 5) or (response[0] != 0x02) or (response[-2] != 0x03) or (checksum != 0x00):
# logger.warning(
# "dlms: checksum/protocol error: response={} checksum={}".format(' '.join(hex(i) for i in response), checksum))
# return
#print(str(response[1:-4], 'ascii'))
for line in re.split('\r\n', str(response[1:-4], 'ascii')):
# if re.match('[0-9]+\.[0-9]\.[0-9](.+)', line): # allows only
# x.y.z(foo)
if re.match('[0-9]+\.[0-9].+(.+)', line): # allows also x.y(foo)
try:
#data = re.split('[(*)]', line)
data = line.split('(')
data[1:3] = data[1].strip(')').split('*')
if (len(data) == 2):
logger.debug("dlms: {} = {}".format(data[0], data[1]))
else:
logger.debug(
"dlms: {} = {} {}".format(data[0], data[1], data[2]))
if data[0] in self._obis_codes:
for item in self._obis_codes[data[0]]['items']:
item(float(data[1]), 'DLMS', 'OBIS {}'.format(data[0]))
except Exception as e:
logger.warning(
"dlms: line={} exception={}".format(line, e))
def parse_item(self, item):
if 'dlms_obis_code' in item.conf:
logger.debug("parse item: {0}".format(item))
obis_code = item.conf['dlms_obis_code']
if not obis_code in self._obis_codes:
self._obis_codes[obis_code] = {'items': [item], 'logics': []}
else:
self._obis_codes[obis_code]['items'].append(item)
return None
checksum und umschalten auskommentiert + typecast + \n vor dem string.
das typecast nach float brauch ich wenn ich mit eval den kWk wert / 1000 dividiere.
welcher EMH ITZ ... hier hängt auch einer ?
gibs da unterschiedliche modele von dem ITZ ? Model 2013, Drehstrom
checksum und umschalten auskommentiert + typecast + \n vor dem string.
das typecast nach float brauch ich wenn ich mit eval den kWk wert / 1000 dividiere.
Ok, das ist das "aktuelle" Plugin.
checksum: wie gesagt, ab jetzt per se optional im "develop"
umschalten: per "baudrate = 300" kann das auch serienmäßig lahmgelegt werden
typecast: hihi, könnte ein "bug" sein - denn eigentlich casten die Items ja auf den korrekten Typ - gut möglich, dass das bei einem eval noch nicht geschehen ist...
\n - hm, könnte man optional machen
Wenn ich dich richtig verstehe willst du so etwas - das funktioniert bei mit leider (noch) nicht - würde das bei dir funktionieren?
Code:
#!/usr/bin/env python3
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
#########################################################################
# Copyright 2011 KNX-User-Forum e.V. https://knx-user-forum.de/
#########################################################################
# DLMS plugin for SmartHome.py. http://mknx.github.io/smarthome/
#
# This plugin 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.
#
# This plugin 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 this plugin. If not, see <http://www.gnu.org/licenses/>.
#########################################################################
import logging
import time
import serial
import re
from time import sleep
logger = logging.getLogger('DLMS')
class DLMS():
def __init__(self, smarthome, serialport, baudrate="auto", update_cycle="60", use_checksum = True):
self._sh = smarthome
self._update_cycle = int(update_cycle)
self._use_checksum = use_checksum
if (baudrate.lower() == 'auto'):
self._baudrate = -1
else:
self._baudrate = int(baudrate)
self._obis_codes = {}
self._serial = serial.Serial(
serialport, 300, bytesize=serial.SEVENBITS, parity=serial.PARITY_EVEN, timeout=2)
self._request = bytearray('\x06000\r\n', 'ascii')
def run(self):
self.alive = True
init_seq = bytes('/?!\r\n', 'ascii')
self._serial.flushInput()
self._serial.write(init_seq)
response = bytes()
prev_length = 0
try:
while self.alive:
response += self._serial.read()
length = len(response)
# break if timeout or newline-character
if (length == prev_length) or ((length > len(init_seq)) and (response[-1] == 0x0a)):
break
prev_length = length
except Exception as e:
logger.warning("dlms: {0}".format(e))
# remove echoed chars if present
if (init_seq == response[:len(init_seq)]):
response = response[len(init_seq):]
if (len(response) >= 5) and ((response[4] - 0x30) in range(6)):
if (self._baudrate == -1):
self._baudrate = 300 * (1 << (response[4] - 0x30))
logger.debug("dlms: meter returned capability for higher baudrate {}".format(self._baudrate))
pow2 = int(self._baudrate / 300)
self._request[2] = 0x30 - 1
while (pow2 > 0):
pow2 >>= 1
self._request[2] += 1
self._sh.scheduler.add('DLMS', self._update_values, prio=5, cycle=self._update_cycle)
def stop(self):
self.alive = False
self._serial.close()
self._sh.scheduler.remove('DLMS')
def _update_values(self):
logger.debug("dlms: update")
start = time.time()
self._serial.baudrate = 300
sleep(0.5)
self._serial.write(self._request)
sleep(1)
self._serial.baudrate = self._baudrate
response = bytes()
prev_length = 0
try:
while self.alive:
response += self._serial.read()
length = len(response)
# break if timeout or "ETX"
if (length == prev_length) or ((length >= 2) and (response[-2] == 0x03)):
break
prev_length = length
except Exception as e:
logger.warning("dlms: {0}".format(e))
logger.debug("dlms: Reading took: {:.2f}s".format(time.time() - start))
# remove echoed chars if present
if (self._request == response[:len(self._request)]):
response = response[len(self._request):]
if self._use_checksum:
# perform checks (start with STX, end with ETX, checksum match)
checksum = 0
for i in response[1:]:
checksum ^= i
if (len(response) < 5) or (response[0] != 0x02) or (response[-2] != 0x03) or (checksum != 0x00):
logger.warning(
"dlms: checksum/protocol error: response={} checksum={}".format(' '.join(hex(i) for i in response), checksum))
return
#print(str(response[1:-4], 'ascii'))
for line in re.split('\r\n', str(response[1:-4], 'ascii')):
# if re.match('[0-9]+\.[0-9]\.[0-9](.+)', line): # allows only
# x.y.z(foo)
if re.match('[0-9]+\.[0-9].+(.+)', line): # allows also x.y(foo)
try:
#data = re.split('[(*)]', line)
data = line.split('(')
data[1:3] = data[1].strip(')').split('*')
if (len(data) == 2):
logger.debug("dlms: {} = {}".format(data[0], data[1]))
else:
logger.debug(
"dlms: {} = {} {}".format(data[0], data[1], data[2]))
if data[0] in self._obis_codes:
for item in self._obis_codes[data[0]]['items']:
item(data[1], 'DLMS', 'OBIS {}'.format(data[0]))
except Exception as e:
logger.warning(
"dlms: line={} exception={}".format(line, e))
def parse_item(self, item):
if 'dlms_obis_code' in item.conf:
logger.debug("parse item: {0}".format(item))
obis_code = item.conf['dlms_obis_code']
if not obis_code in self._obis_codes:
self._obis_codes[obis_code] = {'items': [item], 'logics': []}
else:
self._obis_codes[obis_code]['items'].append(item)
return None
Probiere mal das hier ... wenn ich keinen Denkfehler habe sollte das Echo abgefangen sein. Die Checksumme hab ich mal auskommentiert, hatte keinen commit zum deaktivieren gefunden.
Code:
#!/usr/bin/env python3
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
#########################################################################
# Copyright 2011 KNX-User-Forum e.V. https://knx-user-forum.de/
#########################################################################
# DLMS plugin for SmartHome.py. http://mknx.github.io/smarthome/
#
# This plugin 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.
#
# This plugin 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 this plugin. If not, see <http://www.gnu.org/licenses/>.
#########################################################################
import logging
import time
import serial
import re
logger = logging.getLogger('DLMS')
class DLMS():
def __init__(self, smarthome, serialport, baudrate="auto", update_cycle="60"):
self._sh = smarthome
self._update_cycle = int(update_cycle)
if (baudrate.lower() == 'auto'):
self._baudrate = -1
else:
self._baudrate = int(baudrate)
self._obis_codes = {}
self._serial = serial.Serial(
serialport, 300, bytesize=serial.SEVENBITS, parity=serial.PARITY_EVEN, timeout=2)
self._request = bytearray('\x06000\r\n', 'ascii')
def run(self):
self.alive = True
self._sh.scheduler.add('DLMS', self._update_values,
prio=5, cycle=self._update_cycle)
def stop(self):
self.alive = False
self._serial.close()
self._sh.scheduler.remove('DLMS')
def _update_values(self):
logger.debug("dlms: update")
start = time.time()
init_seq = bytes('/?!\r\n', 'ascii')
logger.debug("Write init")
self._serial.flushInput()
self._serial.write(init_seq)
response = bytes()
prev_length = 0
try:
while self.alive:
response += self._serial.read()
length = len(response)
# break if timeout or newline-character
if (length == prev_length) or ((length > len(init_seq)) and (response[-1] == 0x0a)):
break
prev_length = length
except Exception as e:
logger.warning("dlms: {0}".format(e))
# remove echoed chars if present
if (init_seq == response[:len(init_seq)]):
response = response[len(init_seq):]
if (len(response) >= 5) and ((response[4] - 0x30) in range(6)):
if (self._baudrate == -1):
baud_capable = 300 * (1 << (response[4] - 0x30))
else:
baud_capable = self._baudrate
if baud_capable > self._serial.baudrate:
try:
logger.debug("dlms: meter returned capability for higher baudrate - try to change {}".format(baud_capable))
stx = 'False'
while self.alive and stx == 'False':
response2 = bytes()
self._request[2] = response[4]
# set baudrate to 300 first
time.sleep(0.5)
self._serial.baudrate = 300
self._serial.write(self._request)
# change request to set higher baudrate
time.sleep(0.5)
logger.debug("dlms: trying to switch baudrate")
switch_start = time.time()
self._serial.baudrate = baud_capable
response2 += self._serial.read(1)
if 0x02 in response2:
if len(response2) >= len(self._request):
response2 = response2[len(self_request):]
logger.debug("dlms: cut echo")
logger.debug("dlms: got STX switching took: {:.2f}s".format(time.time() - switch_start))
etx = 'false'
while etx == 'false':
response2 += self._serial.read()
if (0x03 in response2) and (0x21 in response2):
etx = 'true'
stx = 'true'
logger.debug("dlms: got ETX rading took: {:.2f}s".format(time.time() - start))
response = response2
except Exception as e:
logger.warning("dlms: {0}".format(e))
return
#checksum = 0
#for i in response[1:]:
# checksum ^= i
#if (len(response) < 5) or (response[0] != 0x02) or (response[-2] != 0x03) or (checksum != 0x00):
# logger.warning(
# "dlms: checksum/protocol error: response={} checksum={}".format(' '.join(hex(i) for i in response), checksum))
# return
#print(str(response[1:-4], 'ascii'))
for line in re.split('\r\n', str(response[1:-4], 'ascii')):
# if re.match('[0-9]+\.[0-9]\.[0-9](.+)', line): # allows only
# x.y.z(foo)
if re.match('[0-9]+\.[0-9].+(.+)', line): # allows also x.y(foo)
try:
#data = re.split('[(*)]', line)
data = line.split('(')
data[1:3] = data[1].strip(')').split('*')
if (len(data) == 2):
logger.debug("dlms: {} = {}".format(data[0], data[1]))
else:
logger.debug(
"dlms: {} = {} {}".format(data[0], data[1], data[2]))
if data[0] in self._obis_codes:
for item in self._obis_codes[data[0]]['items']:
item(data[1], 'DLMS', 'OBIS {}'.format(data[0]))
except Exception as e:
logger.warning(
"dlms: line={} exception={}".format(line, e))
def parse_item(self, item):
if 'dlms_obis_code' in item.conf:
logger.debug("parse item: {0}".format(item))
obis_code = item.conf['dlms_obis_code']
if not obis_code in self._obis_codes:
self._obis_codes[obis_code] = {'items': [item], 'logics': []}
else:
self._obis_codes[obis_code]['items'].append(item)
return None
Nachdem ich "self._serial=...." in die update-Methode geholt habe geht zumindest jeder zweite Leseversuch. Müsste man evtl. mal weiter debuggen.
Bei dem (jeden zweiten) Versuch der fehlschlägt bleibt dann jeweils das response auf init_seq leer.
Ich tippe auf irgendwas mit open/close wobei das hier (m.M.n. sinnvoll eingesetzt) teilweise nicht nachvollziehbare exceptions wirft.
So ich hab das mal ins develop geschoben und konnte das (hoffentlich) fixen, jeder Versuch klappt hier mit beiden Zählern.
Sorry Mirko, aber das bringt mich echt auf die Palme! Bei MIR funktioniert es nämlich jetzt nicht mehr. Kann doch nicht sein, dass du was ins Repo schiebst, was mit beim nächsten Pull mein Produktivsystem kaputt macht!? Aus genau dem Grund gibt es doch einen "Maintainer" pro Plugin. Ich "reparier" doch auch nicht in anderen Plugins rum!?
Wir hatten uns auch dachte ich drauf geeinigt, dass wir das erst hier ausprobieren? Welchen Sinn macht es jetzt, das wieder auf nen Stand zu bringen der bei MIR funktioniert?
fanta2k hatte auch auf Basis des Repos gearbeitet - was wenn es da jetzt nicht mehr geht?
Mal genauer: Ich kriege NUR NOCH "Try Read Id" - MEHR NICHT.
Wir verarbeiten personenbezogene Daten über die Nutzer unserer Website mithilfe von Cookies und anderen Technologien, um unsere Dienste bereitzustellen. Weitere Informationen findest Du in unserer Datenschutzerklärung.
Indem Du unten auf "ICH stimme zu" klickst, stimmst Du unserer Datenschutzerklärung und unseren persönlichen Datenverarbeitungs- und Cookie-Praktiken zu, wie darin beschrieben. Du erkennst außerdem an, dass dieses Forum möglicherweise außerhalb Deines Landes gehostet wird und bist damit einverstanden, dass Deine Daten in dem Land, in dem dieses Forum gehostet wird, gesammelt, gespeichert und verarbeitet werden.
Kommentar