diff --git a/ebus/__init__.py b/ebus/__init__.py index e4e4539b0..081300bf8 100755 --- a/ebus/__init__.py +++ b/ebus/__init__.py @@ -4,22 +4,23 @@ # Copyright 2018- Martin Sinn m.sinn@gmx.de # Copyright 2012-2013 KNX-User-Forum e.V. http://knx-user-forum.de/ ######################################################################### -# This file is part of SmartHomeNG.py. -# Visit: https://github.com/smarthomeNG/ -# https://knx-user-forum.de/forum/supportforen/smarthome-py +# This file is part of SmartHomeNG. +# https://www.smarthomeNG.de +# https://knx-user-forum.de/forum/supportforen/smarthome-py # -# SmartHomeNG.py is free software: you can redistribute it and/or modify +# SmartHomeNG 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. # -# SmartHomeNG.py is distributed in the hope that it will be useful, +# SmartHomeNG 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 SmartHomeNG.py. If not, see . +# along with SmartHomeNG. If not, see . +# ######################################################################### import logging @@ -36,7 +37,7 @@ class eBus(SmartPlugin): the update functions for the items """ - PLUGIN_VERSION = '1.5.1' + PLUGIN_VERSION = '1.6.0' _items = [] @@ -58,6 +59,9 @@ def __init__(self, sh, *args, **kwargs): returns the value in the datatype that is defined in the metadata. """ + # Call init code of parent class (SmartPlugin) + super().__init__() + logger = logging.getLogger(__name__) # remove for shNG v1.6 self.host = self.get_parameter_value('host') self.port = self.get_parameter_value('port') @@ -95,7 +99,7 @@ def run(self): """ self.logger.debug("Run method called".format(self.get_fullname())) self.alive = True - self.scheduler_add('eBusd', self.refresh, prio=5, cycle=self._cycle, offset=2) + self.scheduler_add(self.get_fullname(), self.refresh, prio=5, cycle=self._cycle, offset=2) def refresh(self): @@ -113,7 +117,7 @@ def refresh(self): value = self.request(request) #if reading fails (i.e. at broadcast-commands) the value will not be updated if 'command not found' not in str(value) and value is not None: - item(value, 'eBus', 'refresh') + item(value, self.get_fullname(), 'refresh') if not self.alive: break @@ -126,8 +130,13 @@ def request(self, request): :type request: str """ if not self.connected: - self.logger.info("eBusd not connected") + self.logger.info("eBusd not connected, try to connect") + self.connect() + + if not self.connected: + self.logger.info("eBusd not connected, giving up") return + self._lock.acquire() try: self._sock.send(request.encode()) @@ -165,7 +174,7 @@ def connect(self): except Exception as e: self._connection_attempts -= 1 if self._connection_attempts <= 0: - self.logger.error('eBus: could not connect to ebusd at {0}:{1}: {2}'.format(self.host, self.port, e)) + self.logger.error('eBus: could not connect to ebusd at {0}:{1}: {2}'.format(self.host, self.port, e)) self._connection_attempts = self._connection_errorlog self._lock.release() return @@ -198,6 +207,7 @@ def stop(self): """ self.logger.debug("Stop method called".format(self.get_fullname())) self.close() + self.scheduler_remove(self.get_fullname()) self.alive = False @@ -209,7 +219,7 @@ def update_item(self, item, caller=None, source=None, dest=None): :param source: if given it represents the source :param dest: if given it represents the dest """ - if caller != 'eBus': + if caller != self.get_fullname(): value = str(int(item())) cmd = item.conf['ebus_cmd'] request = "write -c " + cmd + " " + value + "\n" diff --git a/ebus/README.md.old b/ebus/_pv_1_5_1/README.md old mode 100755 new mode 100644 similarity index 100% rename from ebus/README.md.old rename to ebus/_pv_1_5_1/README.md diff --git a/ebus/_pv_1_5_1/__init__.py b/ebus/_pv_1_5_1/__init__.py new file mode 100644 index 000000000..e4e4539b0 --- /dev/null +++ b/ebus/_pv_1_5_1/__init__.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python3 +# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab +######################################################################### +# Copyright 2018- Martin Sinn m.sinn@gmx.de +# Copyright 2012-2013 KNX-User-Forum e.V. http://knx-user-forum.de/ +######################################################################### +# This file is part of SmartHomeNG.py. +# Visit: https://github.com/smarthomeNG/ +# https://knx-user-forum.de/forum/supportforen/smarthome-py +# +# SmartHomeNG.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. +# +# SmartHomeNG.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 SmartHomeNG.py. If not, see . +######################################################################### + +import logging +import socket +import threading +import time + +from lib.model.smartplugin import * + + +class eBus(SmartPlugin): + """ + Main class of the Plugin. Does all plugin specific stuff and provides + the update functions for the items + """ + + PLUGIN_VERSION = '1.5.1' + + _items = [] + + def __init__(self, sh, *args, **kwargs): + """ + Initalizes the plugin. The parameters descriptions for this method are pulled from the entry in plugin.yaml. + + :param sh: **Deprecated**: The instance of the smarthome object. For SmartHomeNG versions **beyond** 1.3: **Don't use it**! + :param *args: **Deprecated**: Old way of passing parameter values. For SmartHomeNG versions **beyond** 1.3: **Don't use it**! + :param **kwargs:**Deprecated**: Old way of passing parameter values. For SmartHomeNG versions **beyond** 1.3: **Don't use it**! + + If you need the sh object at all, use the method self.get_sh() to get it. There should be almost no need for + a reference to the sh object any more. + + The parameters *args and **kwargs are the old way of passing parameters. They are deprecated. They are imlemented + to support older plugins. Plugins for SmartHomeNG v1.4 and beyond should use the new way of getting parameter values: + use the SmartPlugin method get_parameter_value(parameter_name) instead. Anywhere within the Plugin you can get + the configured (and checked) value for a parameter by calling self.get_parameter_value(parameter_name). It + returns the value in the datatype that is defined in the metadata. + """ + + logger = logging.getLogger(__name__) # remove for shNG v1.6 + self.host = self.get_parameter_value('host') + self.port = self.get_parameter_value('port') + self._cycle = self.get_parameter_value('cycle') + + self._sock = False + self.connected = False + self._connection_attempts = 0 + self._connection_errorlog = 60 + self._lock = threading.Lock() + # self.refresh_cycle = self._cycle # not used + + + def parse_item(self, item): + """ + Default plugin parse_item method. Is called when the plugin is initialized. + The plugin can, corresponding to its attribute keywords, decide what to do with + the item in future, like adding it to an internal array for future reference + :param item: The item to process. + :return: If the plugin needs to be informed of an items change you should return a call back function + like the function update_item down below. An example when this is needed is the knx plugin + where parse_item returns the update_item function when the attribute knx_send is found. + This means that when the items value is about to be updated, the call back function is called + with the item, caller, source and dest as arguments and in case of the knx plugin the value + can be sent to the knx with a knx write function within the knx plugin. + """ + if 'ebus_type' in item.conf and 'ebus_cmd' in item.conf: + self._items.append(item) + return self.update_item + + + def run(self): + """ + Run method for the plugin + """ + self.logger.debug("Run method called".format(self.get_fullname())) + self.alive = True + self.scheduler_add('eBusd', self.refresh, prio=5, cycle=self._cycle, offset=2) + + + def refresh(self): + """ + Refresh items with data from ebusd + """ + for item in self._items: + time.sleep(1) + ebus_type = item.conf['ebus_type'] + ebus_cmd = item.conf['ebus_cmd'] + if ebus_cmd == "cycle": + request = ebus_type + " " + ebus_cmd + "\n" # build command + else: + request = "read" + " -c " + ebus_cmd + "\n" # build command + value = self.request(request) + #if reading fails (i.e. at broadcast-commands) the value will not be updated + if 'command not found' not in str(value) and value is not None: + item(value, 'eBus', 'refresh') + if not self.alive: + break + + + def request(self, request): + """ + send request to ebusd deamon + + :param request: Command to send to ebusd + :type request: str + """ + if not self.connected: + self.logger.info("eBusd not connected") + return + self._lock.acquire() + try: + self._sock.send(request.encode()) + self.logger.debug("REQUEST: {0}".format(request)) + except Exception as e: + self._lock.release() + self.close() + self.logger.warning("error sending request: {0} => {1}".format(request, e)) + return + try: + answer = self._sock.recv(256).decode()[:-2] + self.logger.debug("ANSWER: {0}".format(answer)) + except socket.timeout: + self._lock.release() + self.logger.warning("error receiving answer: timeout") + return + except Exception as e: + self._lock.release() + self.close() + self.logger.warning("error receiving answer: {0}".format(e)) + return + self._lock.release() + return answer + + + def connect(self): + """ + Open socket connection to ebusd deamon + """ + self._lock.acquire() + try: + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._sock.settimeout(2) + self._sock.connect((self.host, self.port)) + except Exception as e: + self._connection_attempts -= 1 + if self._connection_attempts <= 0: + self.logger.error('eBus: could not connect to ebusd at {0}:{1}: {2}'.format(self.host, self.port, e)) + self._connection_attempts = self._connection_errorlog + self._lock.release() + return + self.logger.info('Connected to {0}:{1}'.format(self.host, self.port)) + self.connected = True + self._connection_attempts = 0 + self._lock.release() + + + def close(self): + """ + Close socket connection + """ + self.connected = False + try: + self._sock.shutdown(socket.SHUT_RDWR) + except: + pass + try: + self._sock.close() + self._sock = False + self.logger.info('Connection closed to {0}:{1}'.format(self.host, self.port)) + except: + pass + + + def stop(self): + """ + Stop method for the plugin + """ + self.logger.debug("Stop method called".format(self.get_fullname())) + self.close() + self.alive = False + + + def update_item(self, item, caller=None, source=None, dest=None): + """ + Write items values + :param item: item to be updated towards the plugin + :param caller: if given it represents the callers name + :param source: if given it represents the source + :param dest: if given it represents the dest + """ + if caller != 'eBus': + value = str(int(item())) + cmd = item.conf['ebus_cmd'] + request = "write -c " + cmd + " " + value + "\n" + set_answer = self.request(request) + #just check if set was no broadcast-message + if 'broadcast done' not in set_answer: + request = "read -c " + cmd + "\n" + answer = self.request(request) + #transfer value and answer to float for better comparsion + if float(answer) != float(value) or answer is None: + self.logger.warning("Failed to set parameter: value: {0} cmd: {1} answer {2}".format(value, request, answer)) diff --git a/ebus/_pv_1_5_1/plugin.yaml b/ebus/_pv_1_5_1/plugin.yaml new file mode 100644 index 000000000..dbe420e78 --- /dev/null +++ b/ebus/_pv_1_5_1/plugin.yaml @@ -0,0 +1,79 @@ +# Metadata for the classic-plugin +plugin: + # Global plugin attributes + type: interface # plugin type (gateway, interface, protocol, system, web) + description: + de: 'Unterstützt eBus Heizungen (z.B. Vailant, Wolf, Kromschroeder) - Dieses Plugin verbindet sich zu einem ebusd Deamon (http://www.cometvisu.de/wiki/Ebusd), welcher mit einer eBus Heizung kommuniziert. Voraussetzungen: Ein ebusd Deamon läuft auf dem Netzwerk. (Anmerkung: Der ebusd benötigt ein ebus-Interface um mit ihm zu kommunizieren.' + en: 'Supports eBus heating systems (e.g. Vailant, Wolf, Kromschroeder) - The plugin connects to a ebusd damon (http://www.cometvisu.de/wiki/Ebusd) which is communicating with eBus heatings. Requirements: running ebusd deamon on the network (note: ebusd also requires an ebus-interface)' + maintainer: '? (msinn)' + tester: Sandman60 + state: ready +# keywords: kwd1 kwd2 # keywords, where applicable +# documentation: https://github.com/smarthomeNG/plugins/blob/develop/mqtt/README.md # url of documentation (wiki) page +# support: https://knx-user-forum.de/forum/supportforen/smarthome-py + +# Following entries are for Smart-Plugins: + version: 1.5.1 # Plugin version + sh_minversion: 1.5 # minimum shNG version to use this plugin + #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) + multi_instance: False + restartable: unknown + classname: eBus # class containing the plugin + + +parameters: + # Definition of parameters to be configured in etc/plugin.yaml + + host: + type: ip + default: 127.0.0.1 + description: + de: 'IP Adresse des ebusd Deamons' + en: 'ip address of ebusd deamon' + + port: + type: int + valid_min: 0 + default: 8888 + description: + de: 'Port auf dem der ebusd Deamon lauscht' + en: 'port of ebusd deamon' + + cycle: + type: int + valid_min: 0 + default: 240 + description: + de: 'Cycle Zeit zur Abfrage jedes Items' + en: 'cycle of each item' + +item_attributes: + # Definition of item attributes defined by this plugin + + ebus_cmd: + type: str + description: + de: "ebus_cmd ist das Kommando, welches durch die Telnet Verbindung zum ebusd übertragen wird - z.B. 'cir2 heat_pump_curr', 'mv yield_sum' or 'short hw_load'" + en: "ebus_cmd is the command you use though the telnet-connection to ebusd - e.g. 'cir2 heat_pump_curr', 'mv yield_sum' or 'short hw_load'" + + ebus_type: + type: str + default: 'get' + valid_list: ['get', 'set'] + valid_list_description: + de: ['get', 'set'] + en: ['Items will only be readable, i.e. sensors', 'Items are read/write. All "set"-items will be read cyclic too!'] + description: + de: 'ebus_type legt fest, ob vom ebusd Deamon nur gelesen werden soll oder ob auch Daten an ebusd übertragen werden sollen.' + en: 'ebus_type determins, if data should only be read from the ebusd deamon or if data should be written to ebusd too.' + + +item_structs: NONE + # Definition of item-structure templates for this plugin + +logic_parameters: NONE + # Definition of logic parameters defined by this plugin + +plugin_functions: NONE + # Definition of function interface of the plugin + diff --git a/ebus/plugin.yaml b/ebus/plugin.yaml index 9519b28b0..1cc5e581e 100755 --- a/ebus/plugin.yaml +++ b/ebus/plugin.yaml @@ -3,17 +3,24 @@ plugin: # Global plugin attributes type: interface # plugin type (gateway, interface, protocol, system, web) description: - de: 'Unterstützt eBus Heizungen (z.B. Vailant, Wolf, Kromschroeder) - Dieses Plugin verbindet sich zu einem ebusd Deamon (http://www.cometvisu.de/wiki/Ebusd), welcher mit einer eBus Heizung kommuniziert. Voraussetzungen: Ein ebusd Deamon läuft auf dem Netzwerk. (Anmerkung: Der ebusd benötigt ein ebus-Interface um mit ihm zu kommunizieren.' - en: 'Supports eBus heating systems (e.g. Vailant, Wolf, Kromschroeder) - The plugin connects to a ebusd damon (http://www.cometvisu.de/wiki/Ebusd) which is communicating with eBus heatings. Requirements: running ebusd deamon on the network (note: ebusd also requires an ebus-interface)' + de: > + Unterstützt eBus Heizungen (z.B. Vailant, Wolf, Kromschroeder) + Dieses Plugin verbindet sich zu einem ebusd Deamon (https://ebusd.de/), + welcher mit einer eBus Heizung kommuniziert. + Voraussetzungen: Ein ebusd Deamon läuft auf dem Netzwerk. + Anmerkung: Der ebusd benötigt ein ebus-Interface um mit ihm zu kommunizieren. + en: > + Supports eBus heating systems (e.g. Vailant, Wolf, Kromschroeder) + The plugin connects to a ebusd damon (https://ebusd.de/) + which is communicating with eBus heatings. + Requirements: running ebusd deamon on the network (note: ebusd also requires an ebus-interface)' maintainer: '? (msinn)' - tester: Sandman60 + tester: android, z1marco state: ready -# keywords: kwd1 kwd2 # keywords, where applicable -# documentation: https://github.com/smarthomeNG/plugins/blob/develop/mqtt/README.md # url of documentation (wiki) page -# support: https://knx-user-forum.de/forum/supportforen/smarthome-py + support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1925957 # Following entries are for Smart-Plugins: - version: 1.5.0 # Plugin version + version: 1.6.0 # Plugin version sh_minversion: 1.5 # minimum shNG version to use this plugin #sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest) multi_instance: False diff --git a/ebus/user_doc.rst b/ebus/user_doc.rst new file mode 100644 index 000000000..73ec8d1d1 --- /dev/null +++ b/ebus/user_doc.rst @@ -0,0 +1,99 @@ +.. index:: Plugins; ebus +.. index:: ebus + + +==== +ebus +==== + +Dieses Plugin verbindet sich zu einem ebus daemon und kann über diesen mit Geräten mit eBus Schnittstellen kommunizieren. + +Anforderungen +============= + +Eine ebus Schnittstelle + +Notwendige Software +------------------- + +Ein konfigurierter und funktionierender ebus Daemon der im Netzwerk erreichbar ist. + +Unterstützte Geräte +------------------- + +Beispielsweise Geräte von Vaillant, Wolf, Kromschroeder und andere die über eine ebus Schnittstelle kommunizieren können. + + +Konfiguration +============= + +Die Plugin Parameter und die Informationen zur Item-spezifischen Konfiguration des Plugins sind +unter :doc:`/plugins_doc/config/ebus` beschrieben. + + +plugin.yaml +----------- + +Zu den Informationen, welche Parameter in der ../etc/plugin.yaml konfiguriert werden können bzw. müssen, bitte +bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus +den Metadaten der plugin.yaml erzeugt wurde (siehe oben). + +items.yaml +---------- + +Zu den Informationen, welche Attribute in der Item Konfiguration verwendet werden können bzw. müssen, bitte +bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus +den Metadaten der plugin.yaml erzeugt wurde (siehe oben). + +logic.yaml +---------- + +Zu den Informationen, welche Konfigurationsmöglichkeiten für Logiken bestehen, bitte +bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus +den Metadaten der plugin.yaml erzeugt wurde (siehe oben). + +Funktionen +---------- + +Zu den Informationen, welche Funktionen das Plugin bereitstellt (z.B. zur Nutzung in Logiken), bitte +bitte die Dokumentation :doc:`Dokumentation ` lesen, die aus +den Metadaten der plugin.yaml erzeugt wurde (siehe oben). + +Beispiele +========= + +.. code:: yaml + + ebus_geraet: + + hk_pumpe_perc: + type: num + knx_dpt: 5 + knx_send: 8/6/110 + knx_reply: 8/6/110 + ebus_cmd: cir2 heat_pump_curr + ebus_type: get + # akt. PWM-Wert Heizkreizpumpe + + ernergie_summe: + type: num + knx_dpt: 12 + knx_send: 8/6/22 + knx_reply: 8/6/22 + ebus_cmd: mv yield_sum + ebus_type: get + # Energieertrag + + speicherladung: + type: bool + knx_dpt: 1 + knx_listen: 8/7/1 + ebus_cmd: short hw_load + ebus_type: set + # Quick - WW Speicherladung + + +Web Interface +============= + +Das Plugin hat derzeit kein Web Interface \ No newline at end of file