Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hue plugin - Async fix #394

Merged
merged 14 commits into from
May 3, 2019
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
### __[v#.#]__ - date
##### Added
- Telegram-Plugin: In der generierten Übersichtkarte wird eine Anfahrtsroute integriert. Der Abfahrtsort ist konfiguierbar. [#382](https://github.com/Schrolli91/BOSWatch/pull/382)
- Hue-Plugin: Geräte die mit einer Hue bridge verbunden sind können aus BOSWatch ein- und ausgeschaltet werden. [#394](https://github.com/Schrolli91/BOSWatch/issues/394)
##### Changed
- Telegram-Plugin: Aufrufe der Google API erfolgen per SSL und ohne zusätzliche Bibliotheken [#382](https://github.com/Schrolli91/BOSWatch/pull/382)
##### Deprecated
##### Removed
##### Fixed
- Asynchrone Alarme: Bei asynchroner Verarbeitung von schnell aufeinander folgenden Alarmen, wurde der Inhalt der Objekte typ, freq und data bereits vor dem Abschluss der Verarbeitung eines Alarms wieder überschrieben. Ergebnis hiervon war die Vermischung von RICs und Texten unterschiedlicher Alarme. Lösung über copy.deepcopy() [#394](https://github.com/Schrolli91/BOSWatch/issues/394)
##### Security


Expand Down
23 changes: 22 additions & 1 deletion config/config.template.ini
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ multicastAlarm_ignore_time = 15
# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence). Needs to be empty if multicastAlarms are interrupted by normal alarms.
multicastAlarm_delimiter_ric =

# multicastAlarm RIC that is used to send the text message
# multicastAlarm RIC (one or more, separated by comma) used to send the text message
multicastAlarm_ric =


Expand Down Expand Up @@ -167,6 +167,7 @@ FFAgent = 0
Pushover = 0
Telegram = 0
yowsup = 0
hue = 0

# for developing - template-module
template = 0
Expand Down Expand Up @@ -434,6 +435,26 @@ zvei_message = %DATE% %TIME%: %ZVEI%
#Wildcards can be used, see end of the file!
poc_message = %MSG%

[hue]
# For API access please read https://www.developers.meethue.com/documentation/getting-started (registration required)
# or https://www.google.com/search?q=philips+hue+how+to+get+api+key
# IP address of the hue bridge
bridgeip =

# the numeric ID of the device (only one device supported)
deviceid =

# the authentication string used to access the bridge
apikey =

# Timing parameters for switching on/off
# Every on/off cycle adds delay to BOSwatch if it operates in synchronous mode. Consider to configure "processAlarmAsync = 1" to activate asynchronous operation in BOSwatch if you switch on/off multiple times.
# If a light bulb is connected, you can keep it blinking for a while
repeat = 2
timeon = 2
timeoff = 1
# configure 0 to keep the switch on for infinite time or configure >=1 to keep it for the value in seconds to on, before switching to off.
keepon = 60

#####################
##### Not ready yet #
Expand Down
9 changes: 7 additions & 2 deletions includes/alarmHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import time # timestamp

from includes import globalVars # Global variables
from copy import deepcopy

##
#
Expand All @@ -36,16 +37,20 @@ def processAlarmHandler(typ, freq, data):
@return: nothing
@exception: Exception if starting a Thread failed
"""
#copy objects to avoid issues if the objects will be changed by plugin's or during asynch/threaded processing
dctyp = deepcopy(typ)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ich glaube wenn wir an dieser Stelle kopieren, besteht das Problem weiterhin.
Denn alle Plugins greifen ja auf EXAKT die selbe Kopie zu - oder täusche ich mich?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muss ich mir morgen mal ansehen.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zumindest an dieser Stelle bringt es nichts wenn wir verhindern wollen, dass die Plugins sich gegenseitig die Daten verändern könnten.

Passenderweise müsste es dann wohl in die Schleife, die die Plugins aufruft, sodass jeder Plugin Aufruf eine eigene Kopie der Daten erhält.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guter Punkt. Habe das mal umgebaut. Ich muss das aber noch auf meinem System testen.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@f-kessler Läuft stabil?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Irgendwas läuft da noch nicht richtig. Die Kopien werden offenbar auch überschrieben. Zumindest landen in der Datenbank viele Alarme mit der selben RIC. Ich habe zwei Vermutungen. Entweder es liegt daran, dass in Zeile 44 bereits der Thread erzeugt wird, aber noch mit original Objekten an dieser Stelle gearbeitet wird. Oder es liegt daran, dass die Kopien immer den selben Namen haben und das Kopieren die Kopie überschreibt.
Hast du vielleicht eine Idee dazu?

Copy link
Owner

@Schrolli91 Schrolli91 Mar 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So wie sich mir das darstellt, besteht dieses Problem nur beim data-Dict.

Schlimmer ist es jedoch, wenn Seiteneffekte auftreten, mit denen man nicht gerechnet hat, und dadurch Fehler verursacht werden. Dies kann auch in Python passieren. Dies kann dann geschehen, wenn Listen oder Dictionaries als Parameter übergeben werden.

Die anderen DeepCopys sind also vermutlich gar nicht notwendig.
Evtl reicht es schon das deepCopy direkt in den Funktionsaufruf zu setzen:
plugin.run(typ, freq, deepcopy(data)) ?
Somit würde dann wahrscheinlich die kopierte Variable nicht überschreiben werden, da es keine gibt.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Das ist eine gute Idee. Werde das mal am Wochenende bei mir umschreiben. Hatte inzwischen auch gute Ergebnisse mit einem deepcopy vor dem Aufruf des Threads und einem weiteren deepcopy in der for-Schleife erzielt. Dein Vorschlag ist aber deutlich eleganter als stumpf neue Objekte anzulegen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bin leider erst neulich dazu gekommen deinen Vorschlag zu testen. Bin mit dem Ergebnis sehr zufrieden.

dcfreq = deepcopy(freq)
dcdata = deepcopy(data)
if globalVars.config.getboolean("BOSWatch","processAlarmAsync") == True:
logging.debug("starting processAlarm async")
try:
from threading import Thread
Thread(target=processAlarm, args=(typ, freq, data)).start()
Thread(target=processAlarm, args=(dctyp, dcfreq, dcdata)).start()
except:
logging.error("Error in starting alarm processing async")
logging.debug("Error in starting alarm processing async", exc_info=True)
else:
processAlarm(typ, freq, data)
processAlarm(dctyp, dcfreq, dcdata)


##
Expand Down
122 changes: 122 additions & 0 deletions plugins/hue/hue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/python
# -*- coding: UTF-8 -*-

"""

Plugin to control Philips hue lights and switches

@author: Fabian Kessler

@requires: none
"""

#
# Imports
#
import logging # Global logger
from includes import globalVars # Global variables
import json
import requests
import time

# Helper function, uncomment to use
#from includes.helper import timeHandler
#from includes.helper import wildcardHandler
from includes.helper import configHandler

##
#
# onLoad (init) function of plugin
# will be called one time by the pluginLoader on start
#
def onLoad():
"""
While loading the plugins by pluginLoader.loadPlugins()
this onLoad() routine is called one time for initialize the plugin
@requires: nothing
@return: nothing
@exception: Exception if init has an fatal error so that the plugin couldn't work
"""
try:
########## User onLoad CODE ##########
pass
########## User onLoad CODE ##########
except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)
raise

##
#
# Main function of plugin
# will be called by the alarmHandler
#
def run(typ,freq,data):
"""
This function is the implementation of the Plugin.
If necessary the configuration hast to be set in the config.ini.
@type typ: string (FMS|ZVEI|POC)
@param typ: Typ of the dataset
@type data: map of data (structure see readme.md in plugin folder)
@param data: Contains the parameter for dispatch
@type freq: string
@keyword freq: frequency of the SDR Stick
@requires: If necessary the configuration hast to be set in the config.ini.
@return: nothing
@exception: nothing, make sure this function will never thrown an exception
"""
try:
if configHandler.checkConfig("hue"): #read and debug the config
#for debugging
"""logging.debug(globalVars.config.get("hue", "bridgeip"))
logging.debug(globalVars.config.get("hue", "deviceid"))
logging.debug(globalVars.config.get("hue", "apikey"))
logging.debug(globalVars.config.getint("hue", "repeat"))
logging.debug(globalVars.config.getint("hue", "timeon"))
logging.debug(globalVars.config.getint("hue", "timeoff"))
logging.debug(globalVars.config.getint("hue", "keepon"))"""

########## User Plugin CODE ##########
if typ == "FMS":
logging.warning("%s not supported", typ)
elif typ == "ZVEI":
logging.warning("%s not supported", typ)
elif typ == "POC":
#logging.warning("%s not supported", typ)
logging.debug("POC received")
bridgeip = globalVars.config.get("hue", "bridgeip")
deviceid = globalVars.config.get("hue", "deviceid")
apikey = globalVars.config.get("hue", "apikey")
repeat = globalVars.config.getint("hue", "repeat")
timeon = globalVars.config.getint("hue", "timeon")
timeoff = globalVars.config.getint("hue", "timeoff")
keepon = globalVars.config.getint("hue", "keepon")
data_on = '{"on":true}'
data_off = '{"on":false}'
url = "http://" + bridgeip + "/api/" + apikey + "/lights/" + deviceid + "/state"
logging.debug("hue REST API URL: %s", url)

#blinking
for _ in xrange(repeat):
requests.put(url, data=data_on)
logging.debug("on for %s seconds", timeon)
time.sleep(timeon)
requests.put(url, data=data_off)
logging.debug("off for %s seconds", timeoff)
time.sleep(timeoff)
if keepon > 0:
logging.debug("switch to on and wait for keepon to expire")
requests.put(url, data=data_on)
logging.debug("keep on for %s seconds", keepon)
time.sleep(keepon)
requests.put(url, data=data_off)
else:
logging.debug("switch to on and exit plugin")
requests.put(url, data=data_on)
else:
logging.warning("Invalid Typ: %s", typ)
########## User Plugin CODE ##########

except:
logging.error("unknown error")
logging.debug("unknown error", exc_info=True)
Schrolli91 marked this conversation as resolved.
Show resolved Hide resolved