Danke Martin,
manchmal sieht man dem Wald vor lauter Bäumen nicht.
@ Onkelady ja da hatte ich ausversehen die ( gelöscht. Ist aber schon repariert. Aber Danke für den Hinweis.
VG
Tobias
Ankündigung
Einklappen
Keine Ankündigung bisher.
Problem mit Funktionen und Imports in Logiken
Einklappen
X
-
Und das hier: https://knx-user-forum.de/forum/supp...-einf%C3%BCgen
Und
wird wohl eher auch außerhalb von shng nicht funktionieren..Code:def setWBstatestate):
Einen Kommentar schreiben:
-
Du solltest Dir mal den entsprechenden Abschnitt der Doku zu Gemüte führen: https://www.smarthomeng.de/user/logi...von-funktionen
Einen Kommentar schreiben:
-
Hallo,
ich muss mich hier mal dranhängen da ich ein ähnliches Problem habe.
folgede Logik habe ich geschrieben und diese Funktioniert auch ausserhalb von SH.
Wenn ich das ganze via SH auslösse bekomme ich folgenden Fehler im Log:Code:#!/usr/bin/env python3 # wallboxpv.py import logging import requests import json #Parameter wb_url = "http://192.168.3.204/" pv_url = "http://192.168.3.201/solar_api/v1/GetPowerFlowRealtimeData.fcgi" def setWBstatestate): url = wb_url + "setStatus" r_wb_cur = requests.get(url, params=[('active',state )]) if (state == "true" and r_wb_cur.status_code == 200): print('WB aktiviert') if (state == "false" and r_wb_cur.status_code == 200): print('WB deaktiviert') def change_current(direction): wb_actualCurrent = getWBcurrent() wb_newcurrent = 0 url = wb_url + "setCurrent" if direction == "up": wb_newcurrent = wb_actualCurrent + 1; elif direction == "down": wb_newcurrent = wb_actualCurrent - 1; else: wb_newcurrent = wb_actualCurrent; if wb_newcurrent < 7: setWBstate("false") elif wb_newcurrent > 16: print("Current: "), wb_actualCurrent ,("A mehr geht nicht") else: r_wb_cur = requests.get(url, params=[('current', wb_newcurrent)]) print("Current: ") , wb_actualCurrent , ("A - neu: ") , wb_newcurrent def getPVsoc(): r_wb = requests.get(pv_url) data_wb = r_wb.json() data = data_wb['Body']['Data']['Inverters']['1']['SOC'] #data = 4180 return data def getWBdata(): url = wb_url + "getParameters" r_wb = requests.get(url) data_wb = r_wb.json() data = (data_wb['list'][0]['actualPower']) * 1000 #data = 4180 return data def getPVdata(): r_pv = requests.get(pv_url) data_pv = r_pv.json() data = data_pv['Body']['Data']['Site']['P_Grid'] #data = -100 return data def getWBcurrent(): url = wb_url + "getParameters" r_wb = requests.get(url) data_wb = r_wb.json() data = data_wb['list'][0]['actualCurrent'] return data #auslesen Einspeisung pv_P_Grid = getPVdata() print("PV_Grid: "), pv_P_Grid #Auslesen aktuelle Ladeleistung wb_actualPower = getWBdata() print("WB_P: "), wb_actualPower pv_soc = getPVsoc() print("PV_SOC: "), pv_soc #Logic if pv_P_Grid < 0: if wb_actualPower > 0: if (pv_P_Grid) < -250: change_current("up") elif wb_actualPower == 0: if pv_P_Grid < -1620: setWBstate("true") else: if wb_actualPower > 0: change_current("down") if (pv_soc) < 60: setWBstate("false")
Ich habe auch schon versucht die import anweisung mit in die einzelnen Funktionen rein zu nehmen aber leider mit dem gleichen Ergebniss.Code:2020-07-19 10:18:04 ERROR logics.WallboxPV Logic: logics.WallboxPV, File: /usr/local/smarthome/logics/wallboxpv.py, Line: 57, Method: getPVdata, Exception: name 'requests' is not defined > Traceback (most recent call last): > File "/usr/local/smarthome/lib/scheduler.py", line 527, in _task > exec(obj.bytecode) > File "/usr/local/smarthome/logics/wallboxpv.py", line 71, in <module> > pv_P_Grid = getPVdata() > File "/usr/local/smarthome/logics/wallboxpv.py", line 57, in getPVdata > r_pv = requests.get(pv_url) > NameError: name 'requests' is not defined
Könnt Ihr bitte mal drüber schauen wo der Fehler sein könnte?
Danke
Einen Kommentar schreiben:
-
Im fertigen Ergebnis mache ich nichts anderes, als von Msinn erwähnt. Schlussendlich habe ich einige Funktionen, die wiederum Funktionen aufrufen usw. Die Logik selbst nutze ich, um die Wagenreihung und Verspätung von einem speziellen Zug abzufragen - hierfür benutze ich mehrere DB APIs, die ich zusammenführe.
Das Ergebnis sieht dann ungefähr so aus - ich habe nur die Logik selbst entfernt, da es hier nicht darum geht.
Der "Einstiegspunkt" ist getAndSendZugdata -> hier werden dann bei bedarf weitere Funktionen aufgerufen, die Schlussendlich im logic Objekt zu finden sind (zum Beispiel getString, getChangedTimes, etc.)Code:import urllib.request import json from datetime import date,datetime def getArrivalDeparture(data): pass def getChangedTimes(logic=logic): pass def getString(jsonobj, logic=logic): pass def getCorrectTime(jsonobj,today): pass def getAndSendZugdata(urlpart2, logic=logic): pass logic.getString = getString logic.getChangedTimes = getChangedTimes logic.getArrivalDeparture = getArrivalDeparture today = date.today() try: msg = getAndSendZugdata("endpunkt") sh.telegram(msg,True) except Exception as e: sh.telegram("Unknown Error:\n"+str(e),True)
- Likes 1
Einen Kommentar schreiben:
-
Vielen Dank noch mal für die Erklärung.
Hat jetzt alles wunderbar funktioniert.
Einen Kommentar schreiben:
-
Du könntest auch alle Funktionen und Werte als Parameter übergeben. Das kann aber schnell unübersichtlich werden. Außerdem musst Du beachten, dass Variablen, die Du als Parameter übergibst sich je Typ unterschiedlich verhalten können. Bei einfachen Variablen (string, integer, ...) wird der Wert übergeben wenn dieser in der Funktion verändert wird, wird das nicht in die Original Variable übertragen. Diese bleibt unverändert. Wenn Du allerdings z.B. ein dict oder ein Objekt übergibst, wird dieses per Referenz übergeben. Das bedeutet, in der Funktion vorgenommene Änderungen werden direkt auf dem Original Objekt ausgeführt.
Die Übergabe aller Parameter einzeln hat den Nachteil, dass Du daran denken musst für einen Parameter, den Du in func1 benötigst, diesen auch dem Aufruf von func2 hinzuzufügen, weil Du ihn sonst beim Aufruf von func1 aus func2 heraus nicht übergeben kannst. --> Je verschachtelter, deto komplexer und fehleranfälliger.
Wenn Du alles in das Logik Objekt packst, hast Du überall das gleiche Verhalten. So als hättest Du in der Funktion eine globale Variable angesprochen.
Nachteile in Verbindung mit anderen Logiken gibt es nicht, da jede Logik ihr eigenes privates logic Objekt hat. Da logic Objekt hört NICHT auf zu existieren. Der Sinn liegt gerade darin, Variablen von einem Lauf der Logik zum nächsten Lauf zu "retten". Das logic Objekt hört erst auf zu existieren, wenn Du im Admin Interface die Logik entlädst.
logic1 und logic2 sind Schreibfehler. Das muss in beiden Fällen logic heißen.Zuletzt geändert von Msinn; 07.02.2020, 16:11.
Einen Kommentar schreiben:
-
Msinn, vielen Dank - ich denke, das ist die wahrscheinlich sinnvollste Lösung. Muss ich hierfür das logic Objekt nehmen, oder könnte ich auch alle Funtkionen und Imports separat als Parameter mit Standardwert übergeben?
Gibt es Nachteile, wenn ich das logic Objekt benutze? (Auch in Verbindung mit anderen Logiken?) Oder ist für jede Logik und ihr Aufruf das Logik-Objekt ein separates, welches nach dem Aufruf der Logik aufhört zu existieren?
"logic1 und logic2" <- ist das hier ein Schreibfehler? Ich vermute mal, sonst verwirrt mich der Code an der Stelle nämlichZitat von Msinn Beitrag anzeigenlogic1.func1 = func1
logic2.func2 = func2
Einen Kommentar schreiben:
-
bmx Mit dem was ich genau über Deinem Post gepastet habe, lässt sich das relativ umfassent lösen. Einzige Ergänzung: Du musst auch dir sh Variable durch das logic objekt führen: logic.sh = sh und in den Funktionen halt statt sh dann logic.sh verwenden.
Das war in den frühen Versionen (zu Macus Zeiten) ohne das logic Objekt natürlich umständlicher.
Einen Kommentar schreiben:
-
Loki Das ist wirklich ein Problem was sich nicht in den Griff bekommen läßt. Ich habe damals Marcus auch gefragt und als einzige Quintessenz des ganzen war:
Schreib' Dir ein eigenes Plugin und stelle die Funktionen die Du brauchst per Plugin bereit.
Alternativ kann man auch folgendes machen:
Du kannst das via executor Plugin (develop) ausprobieren.Code:#/usr/local/smarthome/logics/poc.py #!/usr/bin/env python from datetime import datetime import sys class Funktionen(): def __init__(self, locs): for k in locs: setattr(self, k, locs[k]) def func1(self, what): #sh wird mitgegeben today = self.datetime.today() #self.sh.telegram(str(today),True) print("Foobar",what) f = Funktionen({'sh': sh, 'datetime': datetime}) f.func1("foo")
Einen Kommentar schreiben:
-
Da bei diesem Vorgehen jeder Funktion derselbe Parameter mit dem selben Wert übergeben werden muss, erfolgt das am einfachsten, indem dieses als letzter Parameter mit Standardwert erfolgt. Dann braucht bei den Aufrufen der Funktionen der Parameter nicht berücksichtigt zu werden.
Um das Ganze einheitlich zu machen, sollte das für alle Funktionen so gemacht werden, auch wenn diese nicht aus anderen Funktionen aufgerufen werden:
Code:# Definition der Funktionen def func1(val, logic=logic): logger.warning("function 1") logger.warning("value {}".format(val)) def func2(logic=logic): logger.warning("function 2") logic.func1(2) logic.func1 = func1 logic.func2 = func2 # Code der Logik logic.func1(1) logic.func2()Zuletzt geändert von Msinn; 07.02.2020, 16:08.
Einen Kommentar schreiben:
-
Das Verhalten ist durch Python bestimmt. Logiken sind keine Python Module. Es gibt innerhalb der Logik keinen "globalen" Namensraum.
Variablen, die direkt in der Logik definiert werden, sind daher auch keine globalen Variablen. Auf sie kann aus Funktionen innerhalb der Logik nicht zugegriffen werden. Sollen solche Werte in Funtionen genutzt werden, müssen sie als Parameter an die Logik übergeben werden.
Analog müsstest Du func1 als Parameter an func2 übergeben, um func1 innerhalb von func2 aufrufen zu können.
Da dieses Verhalten direkt durch Python vorgegeben ist, ändert es sich auch nicht, wenn Du neuere Versionen als v1.3 von SmartHomeNG einsetzt.
Es gäbe einen Weg, der Dir evtl. helfen könnte (aber wohl erst in neueren Versionen):
Schau mal in die Doku unter https://www-smarthomeng.de/user. Dort sind Variablen beschrieben, den den Lauf einer Logik überstehen. Diese werden im Kontext logic definiert. Diesen Kontext kannst Du auch für Funktionen nutzen:
- Du weist die func1 einer Variablen im Kontext Logik zu (im Beispiel logic.func, der Name ist frei wählbar. Du kannst sie der Übersichtlichkeit halber auch logic.func1 nennen)Code:def func1(): logger.warning("function 1") def func2(logic): logger.warning("function 2") logic.func() logic.func = func1 func2(logic)
- Du übergibst den Kontext logic als Parameter an func2
- Nun kannst Du in func2 auf alle Variablen im Kontext logic zugreifen
- Du rufst logic.func() auf
Das funktioniert zumindest unter SmartHomeNG v1.6 genau so.
- Likes 1
Einen Kommentar schreiben:
-
Problem mit Funktionen und Imports in Logiken
Hallo zusammen,
ich habe aktuell ein Problem mit Imports und Funktionen in Logiken. Eventuell ist es auch nur ein Verständnisproblem - ich hoffe, ihr könnt mir weiterhelfen.
Zum Verstehen des Problems habe ich ein kleines Proof of Concept Skript vorbereitet, welches im folgenden "verschlimmbessert" wird, nur damit es als Smarthomeng Logik funktioniert. Die "Versionsunterschiede" in den Skripten habe ich jeweils mit Kommentaren versehen, damit ihr gleich die Unterschiede erkennen könnt.
Aktuell laufe ich (soweit ich das raus lesen kann) auf SmarthomeNg V 1.3 - also noch eine ältere Version.
Ich bin leider noch nicht dazu gekommen es upzudaten - falls das Problem also bei neueren Versionen gelöst sein sollte wäre das mal ein Grund dazu... Eventuell kann ja jemand mit einer aktuellen Version das Skript ausprobieren, falls das nicht ganz sicher ist.
Version 1 des Poc Skriptes:
So funktioniert es theoretisch, wenn ich es einfach mit Python starte (also ausserhalb von Smarthomeng)
Kurze Erklärung:HTML-Code:#/usr/local/smarthome/logics/poc.py #!/usr/bin/env python from datetime import datetime #import von datetime def func1(): today = datetime.today() sh.telegram(str(today),True) #Ausgabe von Today als String def func2(): func1() #Aufruf von func1 try: func2() #Aufruf von func2 except Exception as e: sh.telegram(str(e),True) #Fehlerausgabe
Func1 holt sich aus datetime das heutige Datum und gibt es via sh.telegram Plugin einem Telegram Bot aus. Ich verwende hier sh.telegram mal als Synonym für die Python eigene print Funktion. Geht hier also lediglich um die Textausgabe!
Func2 ruft Func1 auf.
Im Try Block wird Func2 aufgerufen, bei einem Fehler bekomme ich die Fehlermeldung als Text ausgegeben.
Dieses Skript funktioniert nun also in der normalen cli, wenn ich es mit Python starte (wenn sh.telegram = print!)
Als Smarthomeng Logik funktioniert es nicht. Stattdessen bekomme ich folgenden Fehler:
Smarthomeng erkennt also die Func1 nicht im Func2 Kontext!HTML-Code:name 'func1' is not defined
Version 2:
Ich gebe nun Func1 als Parameter für Func2 mit, damit Func2 Func1 kennt
Ergebnis:HTML-Code:#/usr/local/smarthome/logics/poc.py #!/usr/bin/env python from datetime import datetime def func1(): today = datetime.today() sh.telegram(str(today),True) def func2(f): #func2 bekommt nun eine Funktion als Parameter f() #und ruft diese Funktion auf try: func2(func1) #hier wird func1 als Parameter mitgegeben. Denn hier kennt man func1 except Exception as e: sh.telegram(str(e),True)
Immer noch ein Fehler. Func1 kennt nun datetime nicht. Das Import Statement wird also auch nicht mit in den Kontext der Funktion übernommen.HTML-Code:'module' object has no attribute 'today'
Version 3:
Nun gebe ich den Import in der Funktion mit. Das würde dafür sorgen, dass er plötzlich das sh Objekt nicht kennt (da ich das bisher auch nicht mitgegeben habe). Deshalb ziehe ich auch das sh Objekt via Parameter in den Kontext der Funktionen.
Ergebnis:HTML-Code:#/usr/local/smarthome/logics/poc.py #!/usr/bin/env python def func1(sh): #sh wird mitgegeben from datetime import datetime #import wurde in func1 gezogen today = datetime.today() sh.telegram(str(today),True) def func2(f,sh): #funktion und sh werden mitgegeben f(sh) try: func2(func1,sh) #funktion und sh werden mitgegeben except Exception as e: sh.telegram(str(e),True)
Erst jetzt bekomme ich via Telegram Bot die richtige Text Ausgabe.HTML-Code:2020-02-04 10:36:04.380120
Nun meine Frage.
Gibt es einen besseren Weg, Funktionen und Imports in den Kontext von Funktionen zu ziehen?
Wenn ich ein großes Skriopt mit vielen Funktionen habe, die innerhalb von Funktionen aufgerufen werden (die auch innerhalb von Funktionen aufgerufen werden usw.)
Dann müsste ich immer via Parameterangabe die ganzen Funktionen mit geben, nur damit sie innerhalb von Funktionen genutzt werden können!
Man erkennt schon am Satz, dass das ein großer Sche** ist...
Da kann ich ja komplett auf Funktionen verzichten und einfach eine Prozedur schreiben - wenn ich den gleichen Code nochmal brauche mach ich halt Copy&Paste.
Also, hat jemand schon Erfahrungen damit gemacht und eine saubere Lösung für Imports und Funktionen in Funktionen - in Logiken?
Merci, ich hoffe, ich konnte das Problem einigermaßen verständlich erklären.Zuletzt geändert von Loki; 04.02.2020, 13:59.Stichworte: -

Einen Kommentar schreiben: