Ankündigung
Einklappen
Keine Ankündigung bisher.
Drexel&Weiss Plugin
Einklappen
X
-
Oki, fein, bei mir läuft eigtl. auch immer alles glatt.. mal sehen, wie ich das ins develop bekomme
-
Also auf den ersten Blick funktioniert es ganz wunderbar. Meine Stratos wird beim starten erkannt. Änderungen in SmartVISU werden von der Stratos empfangen und umgesetzt. Auch die Änderungen am Raumcontroller werden in smartHOME empfangen und in smartVISU angezeigt. Super Sache, danke für die Aktualisierung. Ich lass es mal weiter laufen und beobachte.
Einen Kommentar schreiben:
-
Im Prinzip mal aufs Logging.. ob du Warnungen oder Fehler bekommst. Ob eine Verbindung stattfindet und wann genau. Und ob es generell funzt
Wäre cool, wenn du dann mal ein Logfile hier posten könntest.
Am besten im logging.yaml unter loggers DEBUG für plugin.drexelundweiss aktivieren. Das müsste jetzt ja Dank Smart Plugin funzen, hoff ich.
Einen Kommentar schreiben:
-
Ich würde es testen - hab ja schon deine vorige Version im Einsatz. Auf was soll ich genau achten?
Einen Kommentar schreiben:
-
Ich habe das Plugin inzwischen "smart" gemacht.. die "alte" Version läuft bei mir seit Monaten wunderbar, die ursprüngliche Variante ging leider gar nicht.. Möchte jemand das Ganze noch testen? Dann würde ich es als Pull Request in den Develop Branch schieben lassen..
Code:#!/usr/bin/env python3 # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab # # Copyright 2014 KNX-User-Forum e.V. http://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/>. # # encoding=utf8 import logging from lib.model.smartplugin import SmartPlugin import threading import serial import os.path import time import string import re class DuW(SmartPlugin): PLUGIN_VERSION = "1.2.0" ALLOW_MULTIINSTANCE = False def __init__(self, smarthome, tty, LU_ID=130, WP_ID=140, Busmonitor=0, device=0, retrylimit=30): self._sh = smarthome self._LU_ID = LU_ID self._WP_ID = WP_ID self._cmd = False self.LUregl = {} self.WPregl = {} self.LUcmdl = {} self.WPcmdl = {} self.devl = {} self._is_connected = False self._device = int(device) self._retrylimit = int(retrylimit) self._lock = threading.Lock() self.busmonitor = Busmonitor self._pollservice = False self.devl[1] = {'device': 'aerosilent primus', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_primus.txt'} self.devl[2] = {'device': 'aerosilent topo', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_topo.txt'} self.devl[3] = {'device': 'aerosilent micro', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_micro.txt'} self.devl[4] = {'device': 'aerosmart s', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_s.txt'} self.devl[5] = {'device': 'aerosmart m', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_m.txt'} self.devl[6] = {'device': 'aerosmart l', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_l.txt'} self.devl[7] = {'device': 'aerosmart xls', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_xls.txt'} self.devl[8] = {'device': 'aerosilent centro', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_centro.txt'} self.devl[9] = {'device': 'termosmart sc', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/termosmart_sc.txt'} self.devl[10] = {'device': 'x2', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/x2.txt'} self.devl[11] = {'device': 'aerosmart mono', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_mono.txt'} self.devl[13] = {'device': 'aerosilent bianco', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_bianco.txt'} self.devl[14] = {'device': 'x2 plus', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/x2_plus.txt'} self.devl[15] = {'device': 'aerosilent business', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_business.txt'} self.devl[17] = {'device': 'aerosilent stratos', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_stratos.txt'} try: self._port = serial.Serial(tty, 115200, timeout=5) except: self.logger.error("DuW: could not open {}.".format(tty)) return else: self._is_connected = True self._get_device_type() if self._cmd: self._load_cmd() def _convertresponse(self,antwort,teil): antwort = antwort.decode() #self.logger.debug("DuW: Antwort: {}".format(antwort)) allow = string.digits + ' ' antwort = re.sub('[^%s]' % allow, '', antwort) liste = antwort.splitlines() try: antwort = liste[0].split() except: antwort = str("-1") if type(antwort) is list and len(antwort) >= 2: #self.logger.debug("DuW: Ergebnis ist Liste: {0}".format(antwort)) if teil == 'id': antwort = str(antwort[0]) elif teil == 'register': try: antwort = str(antwort[1]) except: antwort = '-1' elif teil == 'data': try: antwort = str(antwort[2]) except: antwort = '-1' else: #self.logger.debug("DuW: Antwort ist Einzelwert: {0}".format(antwort)) antwort = str(antwort) allow = string.digits antwort = re.sub('[^%s]' % allow, '', antwort) self.logger.debug("DuW: Response: {1} = {0}".format(antwort, teil)) return int(antwort) def _get_device_type(self): self.alive = True if self._is_connected: (data, done) = self._read_register('LU\n', 5000, 1, 0) if done: if data in self.devl: self.logger.info("DuW: device: {}".format(self.devl[data]['device'])) if os.path.isfile(self.devl[data]['cmdpath']): self._cmd = self.devl[data]['cmdpath'] self.logger.debug("DuW: Self Command:{}".format(self._cmd)) else: self.logger.error("DuW: no command file found at {}".format(self.devl[data]['cmdpath'])) self._cmd = False else: self.logger.error("DuW: device not supported: {}".format(data)) self._cmd = False else: self.logger.error("DuW: Error reading device type! Trying to activate configured device") if os.path.isfile(self.devl[self._device]['cmdpath']): self._cmd = self.devl[self._device]['cmdpath'] self.logger.info("DuW: device: {0}".format(self.devl[self._device]['device'])) else: self._cmd = False self.logger.error("DuW: no connection") self.alive = False def _send_DW(self, data, pcb): if not self._is_connected: return False if (pcb == 'LU\n'): device_ID = self._LU_ID elif(pcb == 'WP\n'): device_ID = self._WP_ID else: self.logger.error("DuW: wrong pcb description") return if not self._lock.acquire(timeout=2): return try: #self._port.write((str(device_ID) + " " + data + "\r\n").encode()) self._port.write("{0} {1}\r\n".format(device_ID,data).encode()) except Exception as e: self.logger.exception("DuW: Problem sending {0}".format(e)) finally: self._lock.release() def _get_register_info(self, register, pcb): if (pcb == 'LU\n'): if register in self.LUcmdl: return self.LUcmdl[register]['reginfo'] else: return False elif(pcb == 'WP\n'): if register in self.WPcmdl: return self.WPcmdl[register]['reginfo'] else: return False else: self.logger.error("DuW: wrong pcb description") return def _load_cmd(self): self.logger.debug("DuW: Opening command file") f = open(self._cmd, "r") self.logger.debug("DuW: Opened command file") try: for line in f: if not self._lock.acquire(timeout=2): return try: row = line.split(";") # skip first row if (row[1] == "<Description>"): pass else: if row[7] == 'LU\n': self.LUcmdl[int(row[0])] = {'reginfo': row} elif row[7] == 'WP\n': self.WPcmdl[int(row[0])] = {'reginfo': row} else: self.logger.debug("DuW: Error in Commandfile: " + line) except Exception as e: self.logger.exception("DuW: problems loading commands: {0}".format(e)) finally: self._lock.release() finally: f.close() def run(self): if not self._cmd: self.alive = False try: if self._is_connected: self._port.close() except Exception as e: self.logger.exception(e) return self.alive = True # LU registers init for register in self.LUregl: reginfo = self.LUregl[register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.LUregl[register]['items']: (data, done) = self._read_register( reginfo[7], register, int(reginfo[4]), int(reginfo[5])) if done: item(data, 'DuW', 'init process') else: self.logger.debug("DuW: Init LU register failed: {}".format(register)) # WP register init for register in self.WPregl: reginfo = self.WPregl[register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.WPregl[register]['items']: (data, done) = self._read_register( reginfo[7], register, int(reginfo[4]), int(reginfo[5])) if done: item(data, 'DuW', 'init process') else: self.logger.debug("DuW: Init WP register failed: {0}".format(register)) # poll DuW interface dw_id = 0 dw_register = 0 dw_data = 0 response = bytes() self._pollservice = True self._port.flushInput() try: while self.alive: if self._port.inWaiting(): if not self._lock.acquire(timeout=2): return try: response += self._port.read() if (len(response) != 0): if (response[-1] == 0x20 and dw_id == 0): dw_id = self._convertresponse(response,'id') response = bytes() elif (response[-1] == 0x20 and dw_id != 0 and dw_register == 0): dw_register = self._convertresponse(response,'register') response = bytes() elif (response[-1] == 0x0a): dw_data = self._convertresponse(response,'data') if (self.busmonitor): if dw_id == self._LU_ID: if dw_register in self.LUcmdl: reginfo = self.LUcmdl[ dw_register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) self.logger.debug("DuW Busmonitor LU register: {0} {1}: {2}".format(dw_register,reginfo[1],((dw_data / divisor) / (10 ** komma)))) else: self.logger.debug("DuW Busmonitor: unknown LU register: {0} {1}".format(dw_register,dw_data)) elif dw_id == self._WP_ID: if dw_register in self.WPcmdl: reginfo = self.WPcmdl[dw_register][ 'reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) self.logger.debug("DuW Busmonitor WP register: {0} {1}: {2}".format(dw_register,reginfo[1],((dw_data / divisor) / (10 ** komma)))) else: self.logger.debug("DuW Busmonitor: unknown WP register: {0} {1}".format(dw_register,dw_data)) else: self.logger.debug( "DuW Busmonitor: unknown device ID: {}".format(dw_id)) if dw_id == self._LU_ID: if dw_register in self.LUregl: reginfo = self.LUregl[ dw_register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.LUregl[dw_register]['items']: item( ((dw_data / divisor) / (10 ** komma)), 'DuW', 'Poll') else: self.logger.debug("DuW: Ignore LU register {}".format(dw_register)) elif dw_id == self._WP_ID: if dw_register in self.WPregl: reginfo = self.WPregl[ dw_register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.WPregl[dw_register]['items']: item( ((dw_data / divisor) / (10 ** komma)), 'DuW', 'Poll') else: self.logger.debug("DuW: Ignore WP register {}" .format(dw_register)) else: self.logger.debug("DuW: unknown device ID: {}".format(dw_id)) dw_id = 0 dw_register = 0 dw_data = 0 response = bytes() else: response = bytes() dw_id = 0 dw_register = 0 dw_data = 0 self.logger.debug("DuW: Read timeout") except Exception as e: self.logger.exception("DuW: Polling error {0}".format(e)) finally: self._lock.release() time.sleep(0.1) # exit poll service self._pollservice = False except Exception as e: self.logger.exception("DuW: not alive error, {0}".format(e)) def stop(self): self.alive = False # wait until pollservice closed while self._pollservice == True: pass try: if self._is_connected: self._port.close() except Exception as e: self.logger.exception("DuW: Stop Exception, {}".format(e)) def write_DW(self, pcb, register, value): self._send_DW("{0:d} {1:d}".format(int(register), int(value)), pcb) def req_DW(self, pcb, register): self._send_DW("{0:d}".format(int(register)), pcb) def _read_register(self, pcb, register, divisor, komma): if (pcb == 'LU\n'): device_ID = self._LU_ID elif(pcb == 'WP\n'): device_ID = self._WP_ID else: self.logger.error("DuW: wrong pcb description") self._port.flushInput() self.req_DW(pcb, str(register + 1)) response = bytes() dw_id = 0 dw_register = 0 dw_data = 0 retries = 0 if not self._lock.acquire(timeout=2): return try: while self.alive: response += self._port.read() allow = string.digits test = re.sub('[^%s]' % allow, '', str(response.decode())) if len(test) != 0: if (response[-1] == 0x20 and dw_id == 0): dw_id = self._convertresponse(response,'id') response = bytes() elif response[-1] == 0x20 and dw_id != 0 and dw_register == 0: dw_register = self._convertresponse(response,'register') response = bytes() elif response[-1] == 0x0a: dw_data = self._convertresponse(response,'data') break response = bytes() else: retries += 1 self.logger.info("DuW: read timeout: {0}. Retries: {1}".format(response, retries)) if retries >= self._retrylimit: break time.sleep(0.1) except Exception as e: self.logger.warning("DuW: Read error: {0}".format(e)) finally: self._lock.release() if(dw_id == device_ID and (dw_register - 1) == register): self.logger.debug("DuW: Read {1} on Register: {0}".format(register,dw_data)) try: return (((dw_data / divisor) / (10 ** komma)), 1) except: self.logger.debug("Division durch Null Problem") return (((dw_data / 1) / (10 ** 1)), 1) else: self.logger.error("DuW: read errror Device ID: {0}, register {1}".format(dw_id,dw_register - 1)) return (0, 0) def parse_item(self, item): if not self._cmd: return None if self.has_iattr(item.conf, 'DuW_LU_register'): register = int(self.get_iattr_value(item.conf, 'DuW_LU_register')) reginfo = self._get_register_info(register, 'LU\n') if reginfo: if not register in self.LUregl: self.LUregl[register] = {'reginfo': reginfo, 'items': [item]} else: if not item in self.LUregl[register]['items']: self.LUregl[register]['items'].append(item) return self.update_item else: self.logger.warning("DuW: LU register: {} not supported by configured device!".format(register)) return None if self.has_iattr(item.conf, 'DuW_WP_register'): register = int(self.get_iattr_value(item.conf, 'DuW_WP_register')) reginfo = self._get_register_info(register, 'WP\n') if reginfo: if not register in self.WPregl: self.WPregl[register] = {'reginfo': reginfo, 'items': [item]} else: if not item in self.WPregl[register]['items']: self.WPregl[register]['items'].append(item) return self.update_item else: self.logger.warning("DuW: WP register: {} not supported by configured device!".format(register)) return None def update_item(self, item, caller=None, source=None, dest=None): if caller != 'DuW': if self.has_iattr(item.conf, 'DuW_LU_register'): register = int(self.get_iattr_value(item.conf, 'DuW_LU_register')) if register in self.LUregl: reginfo = self.LUregl[register]['reginfo'] data = item() * int(reginfo[4]) * (10 ** int(reginfo[5])) if (data < int(reginfo[2]) or data > int(reginfo[3])): self.logger.error("DuW: value of LU register: {} out of range, changes ignored!".format(register)) pass else: if reginfo[6] == 'R/W': self.logger.debug("DuW: update LU register: {0} {1} with {2}".format(register,reginfo[1],data)) self.write_DW(reginfo[7], register, data) else: self.logger.warning("DuW: tried to update read only LU register: {}".format(register)) if self.has_iattr(item.conf, 'DuW_WP_register'): register = int(self.get_iattr_value(item.conf, 'DuW_WP_register')) if register in self.WPregl: reginfo = self.WPregl[register]['reginfo'] data = item() * int(reginfo[4]) * (10 ** int(reginfo[5])) if (data < int(reginfo[2]) or data > int(reginfo[3])): self.logger.error("DuW: value of WP register {} out of range, changes ignored!".format(register)) pass else: if reginfo[6] == 'R/W': self.logger.debug("DuW: update WP register: {0} {1} with {2}".format(register,reginfo[1],data)) self.write_DW(reginfo[7], register, data) else: self.logger.warning("DuW: tried to update read only WP register: {}".format(register))
Einen Kommentar schreiben:
-
Bernator, vielleicht magst du dir mal meine obigen Änderungen ansehen und das Plugin entsprechend updaten? Wäre cool, wenn die Erweiterungen so aufs Github Develop kämen...
Dabei gibt es zwei neue Attribute in der plugin.conf:Code:#!/usr/bin/env python3 # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab # # Copyright 2014 KNX-User-Forum e.V. http://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 threading import serial import os.path import time import string import re logger = logging.getLogger('') class DuW(): def __init__(self, smarthome, tty, LU_ID=130, WP_ID=140, Busmonitor=0, device=0, retrylimit=100): self._sh = smarthome self._LU_ID = LU_ID self._WP_ID = WP_ID self._cmd = False self.LUregl = {} self.WPregl = {} self.LUcmdl = {} self.WPcmdl = {} self.devl = {} self._is_connected = False self._device = int(device) self._retrylimit = int(retrylimit) self._lock = threading.Lock() self.busmonitor = Busmonitor self._pollservice = False self.devl[1] = {'device': 'aerosilent primus', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_primus.txt'} self.devl[2] = {'device': 'aerosilent topo', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_topo.txt'} self.devl[3] = {'device': 'aerosilent micro', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_micro.txt'} self.devl[4] = {'device': 'aerosmart s', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_s.txt'} self.devl[5] = {'device': 'aerosmart m', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_m.txt'} self.devl[6] = {'device': 'aerosmart l', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_l.txt'} self.devl[7] = {'device': 'aerosmart xls', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_xls.txt'} self.devl[8] = {'device': 'aerosilent centro', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_centro.txt'} self.devl[9] = {'device': 'termosmart sc', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/termosmart_sc.txt'} self.devl[10] = {'device': 'x2', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/x2.txt'} self.devl[11] = {'device': 'aerosmart mono', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosmart_mono.txt'} self.devl[13] = {'device': 'aerosilent bianco', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_bianco.txt'} self.devl[14] = {'device': 'x2 plus', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/x2_plus.txt'} self.devl[15] = {'device': 'aerosilent business', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_business.txt'} self.devl[17] = {'device': 'aerosilent stratos', 'cmdpath': smarthome.base_dir + '/plugins/drexelundweiss/aerosilent_stratos.txt'} try: self._port = serial.Serial(tty, 115200, timeout=5) except: logger.error("DuW: could not open {}.".format(tty)) return else: self._is_connected = True self._get_device_type() if self._cmd: self._load_cmd() def _wait(self,time_lapse): time_start = time.time() time_end = (time_start + time_lapse) while time_end > time.time(): pass def _convertresponse(self,antwort,teil): antwort = antwort.decode() allow = string.digits + ' ' antwort = re.sub('[^%s]' % allow, '', antwort) liste = antwort.splitlines() try: antwort = liste[0].split() except: antwort = str("-1") if type(antwort) is list and len(antwort) >= 2: #logger.debug("DuW Ergebnis ist Liste: {0}".format(antwort)) if teil == 'id': antwort = str(antwort[0]) elif teil == 'register': try: antwort = str(antwort[1]) except: antwort = '-1' elif teil == 'data': try: antwort = str(antwort[2]) except: antwort = '-1' else: #logger.debug("DuW Antwort ist Einzelwert: {0}".format(antwort)) antwort = str(antwort) allow = string.digits antwort = re.sub('[^%s]' % allow, '', antwort) logger.debug("DuW Antwort: {1} = {0}".format(antwort, teil)) return int(antwort) def _get_device_type(self): self.alive = True if self._is_connected: (data, done) = self._read_register('LU\n', 5000, 1, 0) if done: if data in self.devl: logger.info("DuW: device: " + self.devl[data]['device']) if os.path.isfile(self.devl[data]['cmdpath']): self._cmd = self.devl[data]['cmdpath'] else: logger.error( "DuW: no command file found at: " + self.devl[data]['cmdpath']) self._cmd = False else: logger.error("DuW: device not supported: " + str(data)) self._cmd = False else: logger.error("DuW: Error reading device type! Trying to activate configured device") if os.path.isfile(self.devl[self._device]['cmdpath']): self._cmd = self.devl[self._device]['cmdpath'] logger.info("DuW: device: {0}".format(self.devl[self._device]['device'])) #self._cmd = False else: self._cmd = False logger.error("DuW: Keine Vrbindung") self.alive = False def _send_DW(self, data, pcb): if not self._is_connected: return False if (pcb == 'LU\n'): device_ID = self._LU_ID elif(pcb == 'WP\n'): device_ID = self._WP_ID else: logger.error("wrong pcb description") return if not self._lock.acquire(timeout=2): return try: self._port.write((str(device_ID) + " " + data + "\r\n").encode()) except Exception as e: logger.exception("Drexel sendDW: {0}".format(e)) finally: self._lock.release() def _get_register_info(self, register, pcb): if (pcb == 'LU\n'): if register in self.LUcmdl: return self.LUcmdl[register]['reginfo'] else: return False elif(pcb == 'WP\n'): if register in self.WPcmdl: return self.WPcmdl[register]['reginfo'] else: return False else: logger.error("wrong pcb description") return def _load_cmd(self): f = open(self._cmd, "r") try: for line in f: if not self._lock.acquire(timeout=2): return try: row = line.split(";") # skip first row if (row[1] == "<Description>"): pass else: if row[7] == 'LU\n': self.LUcmdl[int(row[0])] = {'reginfo': row} elif row[7] == 'WP\n': self.WPcmdl[int(row[0])] = {'reginfo': row} else: logger.debug("DuW: Error in Commandfile: " + line) except Exception as e: logger.exception("Drexel loadCMD: {0}".format(e)) finally: self._lock.release() finally: f.close() def run(self): if not self._cmd: self.alive = False try: if self._is_connected: self._port.close() except Exception as e: logger.exception(e) return self.alive = True # LU registers init for register in self.LUregl: reginfo = self.LUregl[register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.LUregl[register]['items']: (data, done) = self._read_register( reginfo[7], register, int(reginfo[4]), int(reginfo[5])) if done: item(data, 'DuW', 'init process') else: logger.debug("Drexel: Init LU register: " + str(register) + " failed!") # WP register init for register in self.WPregl: reginfo = self.WPregl[register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.WPregl[register]['items']: (data, done) = self._read_register( reginfo[7], register, int(reginfo[4]), int(reginfo[5])) if done: item(data, 'DuW', 'init process') else: logger.debug("Drexel: Init WP register: " + str(register) + " failed!") # poll DuW interface dw_id = 0 dw_register = 0 dw_data = 0 response = bytes() self._pollservice = True self._port.flushInput() try: while self.alive: if self._port.inWaiting(): if not self._lock.acquire(timeout=2): return try: response += self._port.read() if (len(response) != 0): if (response[-1] == 0x20 and dw_id == 0): dw_id = self._convertresponse(response,'id') #logger.debug("Drexel dw_id: "+(str(dw_id))) response = bytes() elif (response[-1] == 0x20 and dw_id != 0 and dw_register == 0): dw_register = self._convertresponse(response,'register') #logger.debug("Drexel dw_register: "+(str(dw_register))) response = bytes() elif (response[-1] == 0x0a): dw_data = self._convertresponse(response,'data') #logger.debug("Drexel dw_data: "+(str(dw_data))) if (self.busmonitor): if dw_id == self._LU_ID: if dw_register in self.LUcmdl: reginfo = self.LUcmdl[ dw_register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) logger.debug("DuW Busmonitor LU register: " + str(dw_register) + " " + str(reginfo[1]) + ": " + str(((dw_data / divisor) / (10 ** komma)))) else: logger.debug( "DuW Busmonitor: unknown LU register: " + str(dw_register) + " " + str(dw_data)) elif dw_id == self._WP_ID: if dw_register in self.WPcmdl: reginfo = self.WPcmdl[dw_register][ 'reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) logger.debug("DuW Busmonitor WP register: " + str(dw_register) + " " + str(reginfo[1]) + ": " + str(((dw_data / divisor) / (10 ** komma)))) else: logger.debug( "DuW Busmonitor: unknown WP register: " + str(dw_register) + " " + str(dw_data)) else: logger.debug( "DuW Busmonitor: unknown device ID: " + str(dw_id)) if dw_id == self._LU_ID: if dw_register in self.LUregl: reginfo = self.LUregl[ dw_register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.LUregl[dw_register]['items']: item( ((dw_data / divisor) / (10 ** komma)), 'DuW', 'Poll') else: logger.debug( "Ignore LU register " + str(dw_register)) elif dw_id == self._WP_ID: if dw_register in self.WPregl: reginfo = self.WPregl[ dw_register]['reginfo'] divisor = int(reginfo[4]) komma = int(reginfo[5]) for item in self.WPregl[dw_register]['items']: item( ((dw_data / divisor) / (10 ** komma)), 'DuW', 'Poll') else: logger.debug( "Ignore WP register " + str(dw_register)) else: logger.debug( "DuW Busmonitor: unknown device ID: " + str(dw_id)) dw_id = 0 dw_register = 0 dw_data = 0 response = bytes() else: response = bytes() dw_id = 0 dw_register = 0 dw_data = 0 logger.debug("DuW read timeout: ") except Exception as e: logger.exception("Drexel Polling: {0}".format(e)) finally: self._lock.release() self._wait(0.1) # exit poll service self._pollservice = False except Exception as e: logger.exception("Drexel nicht alive: {0}".format(e)) def stop(self): self.alive = False # wait until pollservice closed while self._pollservice == True: pass try: if self._is_connected: self._port.close() except Exception as e: logger.exception(e) def write_DW(self, pcb, register, value): self._send_DW("{0:d} {1:d}".format(int(register), int(value)), pcb) def req_DW(self, pcb, register): self._send_DW("{0:d}".format(int(register)), pcb) def _read_register(self, pcb, register, divisor, komma): if (pcb == 'LU\n'): device_ID = self._LU_ID elif(pcb == 'WP\n'): device_ID = self._WP_ID else: logger.error("wrong pcb description") self._port.flushInput() self.req_DW(pcb, str(register + 1)) response = bytes() dw_id = 0 dw_register = 0 dw_data = 0 retries = 0 if not self._lock.acquire(timeout=2): return try: while self.alive: response += self._port.read() allow = string.digits test = re.sub('[^%s]' % allow, '', str(response.decode())) if len(test) != 0: if (response[-1] == 0x20 and dw_id == 0): dw_id = self._convertresponse(response,'id') #logger.debug("Drexel Reading dw_id: "+str(dw_id)) response = bytes() elif response[-1] == 0x20 and dw_id != 0 and dw_register == 0: dw_register = self._convertresponse(response,'register') #logger.debug("Drexel Reading dw_register: "+str(dw_register)) response = bytes() elif response[-1] == 0x0a: dw_data = self._convertresponse(response,'data') #logger.debug("Drexel Reading dw_data: "+str(dw_data)) break response = bytes() else: retries += 1 self._wait(0.2) logger.info("Drexel read timeout: {0}. Retries: {1}".format(response, retries)) if retries >= self._retrylimit: break self._wait(0.1) except Exception as e: logger.warning("Drexel Reading: {0}".format(e)) finally: self._lock.release() if(dw_id == device_ID and (dw_register - 1) == register): logger.debug("Drexel: Read {1} on Register: {0}".format(register,dw_data)) try: return (((dw_data / divisor) / (10 ** komma)), 1) except: logger.debug("Division durch Null Problem") return (((dw_data / 1) / (10 ** 1)), 1) else: logger.error("DuW read errror Device ID: " + str(dw_id) + " register: " + str(dw_register - 1)) return (0, 0) def parse_item(self, item): if not self._cmd: return None if 'DuW_LU_register' in item.conf: register = int(item.conf['DuW_LU_register']) reginfo = self._get_register_info(register, 'LU\n') if reginfo: if not register in self.LUregl: self.LUregl[register] = {'reginfo': reginfo, 'items': [item]} else: if not item in self.LUregl[register]['items']: self.LUregl[register]['items'].append(item) return self.update_item else: logger.warning("Drexel: LU register: " + str(register) + " not supported by configured device!") return None if 'DuW_WP_register' in item.conf: register = int(item.conf['DuW_WP_register']) reginfo = self._get_register_info(register, 'WP\n') if reginfo: if not register in self.WPregl: self.WPregl[register] = {'reginfo': reginfo, 'items': [item]} else: if not item in self.WPregl[register]['items']: self.WPregl[register]['items'].append(item) return self.update_item else: logger.warning("Drexel: WP register: " + str(register) + " not supported by configured device!") return None def update_item(self, item, caller=None, source=None, dest=None): if caller != 'DuW': if 'DuW_LU_register' in item.conf: register = int(item.conf['DuW_LU_register']) if register in self.LUregl: reginfo = self.LUregl[register]['reginfo'] data = item() * int(reginfo[4]) * (10 ** int(reginfo[5])) if (data < int(reginfo[2]) or data > int(reginfo[3])): logger.error("DuW: value of LU register: " + str(register) + " out of range, changes ignored!") pass else: if reginfo[6] == 'R/W': logger.debug("DuW: update LU register: " + str(register) + " " + reginfo[1] + " with: " + str(data)) self.write_DW(reginfo[7], register, data) else: logger.warning( "DuW: tried to update read only LU register: " + str(register)) if 'DuW_WP_register' in item.conf: register = int(item.conf['DuW_WP_register']) if register in self.WPregl: reginfo = self.WPregl[register]['reginfo'] data = item() * int(reginfo[4]) * (10 ** int(reginfo[5])) if (data < int(reginfo[2]) or data > int(reginfo[3])): logger.error("DuW: value of WP register: " + str(register) + " out of range, changes ignored!") pass else: if reginfo[6] == 'R/W': logger.debug("DuW: update LU register: " + str(register) + " " + reginfo[1] + " with: " + str(data)) self.write_DW(reginfo[7], register, data) else: logger.warning( "DuW: tried to update read only WP register: " + str(register))
Device ist die entsprechende Nummer. Diese wird als Fallback herangezogen, wenn beim Start die Device ID nicht ausgelesen werden kann. Kam bei mir öfters vor, nicht nachvollziehbar.Code:device = 14 retrylimit = 100
Retrylimit definiert die Anzahl an neuen Leseversuchen. Hier habe ich mi 100 eine hohe Zahl, meist erhalte ich sofort oder nach ca. 20 Leseversuchen einen korrekten Wert..
Einen Kommentar schreiben:
-
Hab die Änderungen von Onkelandy ebenfalls verwenden müssen - komischerweise hat es mit der alten Smarthome Version ohne Probleme funktioniert. Kann es sein, dass SmarthomeNG eine andere Python Version voraussetzt bzw. benutzt? Ich hab von 2.7 bis 3.4 alles auf meinem System :-)
Einen Kommentar schreiben:
-
Soda, hab mich jetzt mit meinen rudimentären Python Kenntnissen doch noch dran gewagt.
Problem ist, dass ab und zu blödsinnige Antworten kommen oder vermutlich eine Verzögerung in der Übertragung dafür sorgt, dass die Responses nicht sauber getrennt werden. Vermutlich könnte man hier im Abfrage-Code direkt noch was optimieren. Ich habe meine Probleme aber nun wie folgt gelöst:
* Unknown Register 1348: Habe hier einfach ins txt File das Register eingefügt und ein Item angelegt. Was es bedeutet, weiß ich nicht...
* Division by zero: Im txt File habe ich beim HEIZUNG+ den Divisor auf 1 statt 0 gesetzt. Zusätzlich im Plugin-Code das Problem mittels try und except abgefangen.
* Kein Erkennen des Geräts... Problem resultiert wohl wie oben beschrieben darin, dass manchmal die Daten nicht gleich beim ersten Mal richtig kommen.. beim 2. oder 3. Mal aber dann schon. Manchmal gab es eine Reihe an "\x00" vor dem eigentlichen Wert, ab und an offenbar auch mehrere Zeilen am Stück, als "\x00130 5000 14\r\n\x00\x00130 5000 14 etc.". Das fange ich jetzt durch folgende, vermutlich etwas komplizierte Funktion ab:
Damit die Funktion aufgerufen wird, hab ich im normalen Code dasCode:def _convertresponse(self,antwort,teil): antwort = antwort.decode() allow = string.digits + ' ' antwort = re.sub('[^%s]' % allow, '', antwort) liste = antwort.splitlines() try: antwort = liste[0].split() except: antwort = str(antwort) if type(antwort) is list and len(antwort) >= 2: if teil == 'id': antwort = str(antwort[0]) elif teil == 'register': try: antwort = str(antwort[1]) except: antwort = '-1' elif teil == 'data': try: antwort = str(antwort[2]) except: antwort = '-1' else: antwort = str(antwort) allow = string.digits antwort = re.sub('[^%s]' % allow, '', antwort) #logger.debug("DuW Antwort: {0} fuer Teil {1}".format(antwort, teil)) return int(antwort)
dw_data = int(response)
durch dw_data = self._convertresponse(response,'data')
ersetzt.
*Keine bzw. leere Antwort: Hier habe ich Zeile 415 adaptiert. Wenn eine leere Antwort zurück kommt, soll nochmals nachgehakt werden. Nach 100 Versuchen wird abgebrochen..
Soweit so gut. Es lässt sich dadurch immer eine stabile Verbindung aufbauen. Ansteuern von Werten über die CLI oder Visu ist kein Problem, auch das Auslesen der Daten beim Start des Plugins nicht.Code:else: retries += 1 logger.debug("Drexel read timeout: {0}. Retries: {1}".format(response, retries)) if retries >= 100: break
z.B.: Item LU.CO2_Stufe2 = 800.0 via DuW init process None
Allerdings werden die Items nach einer Änderung am Panel nicht wirklich aktualisiert...
Offenbar wird eine Änderung am Panel zwar registriert.. aber nicht von ID 130 bzw. 140:
2016-06-12 13:41:02,774 DEBUG DuW DuW Antwort: 120 fuer Teil id -- __init__.py:_convertresponse:118
2016-06-12 13:41:03,300 DEBUG DuW DuW Antwort: 5016 fuer Teil register -- __init__.py:_convertresponse:118
2016-06-12 13:41:04,019 DEBUG DuW DuW Antwort: 22000 fuer Teil data -- __init__.py:_convertresponse:118
2016-06-12 13:41:04,026 DEBUG DuW DuW Busmonitor: unknown device ID: 120 -- __init__.py:run:335
Sollte hier nicht gleich ein Update vom Gerät selbst gesendet werden?
Merci!
Ach ja.. sonderbarerweise kommen direkt nach einem Reboot nur "\x00" daher, wenn der Gerätetyp abgefragt wird. Und das, obwohl das smarthome-Service erst startet, wenn die USB-Verbindung vorhanden ist. Sehr komisch...Zuletzt geändert von Onkelandy; 13.06.2016, 21:49.
Einen Kommentar schreiben:
-
Habe hier eine aktuelle Liste mit Parametern gefunden:
http://filter.drexel-weiss.at/HP/Upl...r_V4.01_DE.pdf
Hab gerade die gleiche Liste per Mail zugesandt bekommen, ist also aktuell.Zuletzt geändert von Onkelandy; 13.06.2016, 10:40.
Einen Kommentar schreiben:
-
DuW read errror Device ID: 130 register: 5000
Beim Start wird versucht das register 5000 auszulesen, hier steht die Information um welches Gerät es sich handelt.... Scheinbar git es da ein Kommunikationsproblem, passiert das auch wenn das D&W Gerät bereits länger läuft? Bei mir hab ich beobachtet das nach einem Reset von D&W Gerät + Smarthome.py (Stromausfall) das D&W Gerät scheinbar langsamer Startet und daher Smarthome.py zu früh die ID abruft....
Modbus-Doku hab ich leider auch nichts aktuelleres....
Einen Kommentar schreiben:
-
Hi!
Ich nutze SmarthomeNG 1.1 mit Python 3.4.2 und erhalte manchmal folgende Fehlermeldung:
Bei manchem Start geht's durch und ich erhalte folgende Rückmeldung...:Code:Drexel: invalid literal for int() with base 10: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00130 '
Generell scheint es aber gut zu funktionieren und ich kann auch alles steuern wie ich möchte - 1000 Dank schon mal dafür.Code:2016-06-03 15:13:26,035 DEBUG Main Drexel dw_id: 130 -- __init__.py:_read_register:371 2016-06-03 15:13:26,546 DEBUG Main Drexel dw_register: 5001 -- __init__.py:_read_register:376 2016-06-03 15:13:26,957 DEBUG Main Drexel dw_data: 14 -- __init__.py:_read_register:381 2016-06-03 15:13:26,962 ERROR Main DuW read errror Device ID: 130 register: 5000 -- __init__.py:_read_register:398 2016-06-03 15:13:26,970 ERROR Main DuW: Error reading device type! -- __init__.py:_get_device_type:114
Irgendeine Idee, wie ich die seltenen Problemfälle lösen kann? Cool wäre auch, wenn wer einen Link zur aktuellsten Modbus-Doku hätte, wo ich die aktuellen Register finde. Denn offenbar ist mit den neueren Firmwares noch ein bisschen was dazu gekommen an Funktionen.. insbesondere WP register 1348
Vielen Dank!!!
Einen Kommentar schreiben:
-
Ich verwende ein Kabel mit gewinkeltem USB Stecker dann gehts sichs aus.....
Einen Kommentar schreiben:
-
Bin zwar nicht Bernhard.. aber kannst du das Gehäuse nicht aufschrauben von vorne? Dann solltest du Zugang zum USB Anschluss auf dem Board haben. Das Kabel kannst du dann nach hinten zusammen mit Strom und Co raus führen.
Einen Kommentar schreiben:
-
Hallo Bernhard,
Erst einmal besten Dank für deine Arbeit. Seit kurzem ist meine aerosilent stratos installiert. Technisch bedingt kann ich jedoch den USB Stecker bei geschlossenem Gehäuse nicht nutzen. Die Abdeckung verhindert das. Wie hat du das Problem mit dem USB Stecker gelöst?
Besten Dank
Raphael
Einen Kommentar schreiben:


Einen Kommentar schreiben: