-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ebus: try connection on request, add userdoc.rst
- Loading branch information
Showing
6 changed files
with
437 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,22 +4,23 @@ | |
# Copyright 2018- Martin Sinn [email protected] | ||
# 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 <http://www.gnu.org/licenses/>. | ||
# along with SmartHomeNG. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
######################################################################### | ||
|
||
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" | ||
|
0
ebus/README.md.old → ebus/_pv_1_5_1/README.md
100755 → 100644
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
#!/usr/bin/env python3 | ||
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab | ||
######################################################################### | ||
# Copyright 2018- Martin Sinn [email protected] | ||
# 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 <http://www.gnu.org/licenses/>. | ||
######################################################################### | ||
|
||
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)) |
Oops, something went wrong.