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




Kommentar