smartHome Zeitschaltuhr 2.0

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Hallo,

ich möchte euch hier die neue Version meines smartHome Systems vorstellen.
Im Moment ist es nicht mehr als eine Zeitschaltuhr, aber ich arbeite weiter daran.

Das besonder an der neuen Version ist die multi-client-Fähigkeit, d.h. es können beliebig viele Raspberry Pi's/PC's von einem server-Skript aus angesteuert werden.
Und verwaltet wird das Ganze über ein html5-Webfrontend.

Am besten einfach mal in meinem Blog vorbeischauen:
http://www.mausbiber-projekte.de/archive/546

PS: Über Hilfe würde ich mich sehr freuen, es gibt viele Baustellen und ich bin nicht wirklich gut im programmieren :wink:
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

So, habe ich habe mich jetzt auch mal an Projekt-Spezifikationen für die Software versucht.
Wenn es interessiert, einfach mal hier reinschauen:

http://www.mausbiber-projekte.de/smarth ... ikationen
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ob du dein Ziel der Kollaboration mit der Spezifikation erreichst, kann ich schwer beurteilen.

Aber ein paar Anmerkungen habe ich:

- das die clients in deinem Diagramm Zugriff auf die Datenbank erhalten - und nicht nur der Server - halte ich fuer uengluecklich. Das deutet eine Menge Probleme in meinen Augen: als client-Entwickler muss ich *zwei* APIs - Server & DB - bedienen, eine davon nagelt mich auch noch auf eine spezifische Datenbanktechnologie fest, und last but not least kann ich damit wahrscheinlich dieselben Dinge auf mindestens zwei, im Zweifel auf gemischet 3000 Arten erledigen. Standardisiere das auf das JSON-basierte Netzwerkprotokoll.

- die Darstellung der JSON-Formate als Tabelle ist unorthodox, und vermittelt mir ueberhaupt nicht, was du da erwartest. Zahlwerte in deutsche Schreibung mit Komma haben in JSON entweder nichts verloren, oder werden ineffizient als Strings uebertragen - kann ich mir fast nicht vorstellen. Echte JSON-Nachrichten sind da sinnvoller. Spaeter sieht man, dass du immer Arrays meinst. Halte ich fuer wenig sinnvoll - statt Reihenfolge sollte man lieber ein Objektliteral mit diversen definierten und ggf. optionalen Schluesseln waehlen.

- es gibt in Python Stringliterale mit dreifachen Anfuehrungszeichen - damit schreibt man mehrzeilige Strings einfach, statt so eine \-Verkettungsorgie zu feiern.

- wirklich *einfach* erweiterbar ist das System laut des gezeigten Quellcodes nicht. Die grosse if-Kaskade, um Schaltertypen anzulegen, ist unschoen. Vor allem hast du an anderen Stellen standardisierte Schnittstellen, mit generisch benannten Argumenten irgendeiner Art - aber mit einem mal bekommen alle Klassen ihre ganz persoenlichen Argumente uebergeben. Wenn das standardisiert abliefe, koenntest du eine simples Factory-Pattern verwenden, und das ggf. mit einem Plugin-System kombinieren, um neue Schalter-Typen anzulegen. Ob das sinnvoll ist, kann ich schwer beurteilen. Ich glaube ja, es waere zielfuehrender, ueber die entsprechende Server JSON API diverse Typen - an/aus, stufenloser regler usw - anzubieten - und das war's dann auch schon. Gar kein Grund, da grossartig Code fuer zu schreiben - ob hinter einem Schalter nun ein PI oder ein Arduino oder was auch immer steckt, muss den Server meistenteils doch gar nicht interessieren. Da sind viel eher Fragen nach Gruppierung und Automatisierung relevant.

Dafuer wandern dann deine ganzen Tinkerforge-dieses und GPIO-jenes in eigenstaendig laufende Programme, die wissen dann halt, was sie wissen muessen.

- im Quellcode habe ich property-Deklarationen gefunden, die nicht mit Dekorator gemacht wurden - das ist meiner Erinnerung schon seit Python 2.4, also ungefaehr 12 Jahren, nicht mehr so ueblich.

- es findet sich Code wie folgender:

Code: Alles auswählen

        if arg_b == "b switch":
            self.bricklet.switch_socket_b(int(arg_c), int(arg_d), switch_to)
            switched = True
            pass
        elif arg_b == "c switch":
            self.bricklet.switch_socket_c(int(arg_c), int(arg_d), switch_to)
            switched = True
            pass
            
        if switched:
            # tu was
Da ist viel komisch. Es faengt an mit dem komplett identischen Code fuer beide Faelle - da sollte also eine vereinheitlichte Bedingung benutzt werden, und nur einmal der Code. Das pass ist ueberfluessig. Die switched-Variable wird danach sofort benutzt in einem if - sie koennte also weg, und der entsprechende Code einfach eingerueckt darunter. Die Konstanten sind unerklaert und magisch, die Werte (mit Leerzeichen) eher unueblich gewaehlt, die generischen Argumentnamen unklar.

- wann ein Attribut bei dir privat oder oeffentlich ist folgt keinem mir erkennbaren Schema

So. Jetzt ab ins Bett. Nicht boese sein - du hast um Feedback gebeten :)
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Wow ...

Also nein, böse bin ich wirklich nicht, eher sehr erfreut das sich da wohl jemand auskennt.

Leider kann ich mit vielem was du schreibst nicht wirklich viel anfangen, ich verstehe es nicht, bzw. kenne die Methoden & Funktionen nicht.
Kurz gesagt: ich bin überfordert.

Wie du sicherlich gemerkt hast, habe ich wenig Ahnung vom programmieren und noch weniger von python.
Ich arbeite mit Beispielen aus dem Internet, Hilfe aus Foren und viel, viel "Try & Error".

Genau deshalb suche ich ja Hilfe, 1-2 Profis die sich (mit mir?) mal kurz ran setzen und die gröbsten Schnitzer raus hauen.

Hmm, ich muss mir das morgen früh wenn ich wacher bin noch einmal durchlesen und hoffe dann wenigstens 1-2 Ansätze zu finden die für mich machbare sind.

Aber auf jeden Fall DANKE :)
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Naja, ein wenig frustriert bin ich schon.
Weil wenn wir ehrlich sind, dann ist das Ganze bisher wohl eher ein zusammen gewürfelter Haufen Mist :?

So, jetzt gehts aber schlafen ... und morgen mit neuem Optimismus frisch ans Werk :wink:
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na, Misthaufen ist schon sehr streng. Was in meinen Augen ein guter Anfang waere: wenn du - wie du schreibst, und was ja auch sinnvoll ist - client-server moechtest, dann sollte man sich erst einmal klar machen, was die bestehenden Komponenten abstrakt koennen.

Also zb ein Schalter:

- hat einen Zustand (on/off)
- kann der Zustand gemeldet werden? Oder muss man den definiert setzen, und selbst nachhalten, wie er steht?
- hat einen Namen, Beschreibung, eindeutige ID?
- kann geschaltet werden
- kann (wenn Frage oben nach der Rueckmeldung mit ja beantwortet wurde) automatisch "kippen"? (das koennte zb ein Notaus einer Pumpe mit Ueberlastungsschutz oder so etwas sein)
- kann Fehler als Text/Code melden?

Wenn man sich da erst mal im klaren drueber geworden ist, was das so alles bedeutet, definiert man dafuer ein Protokoll. Wie meldet der Schalter sich beim Server an, was wird zyklisch und ereignisbasiert ausgetauscht usw. Wenn man sich das ueberlegt hat (zumindest im groben), baut man eine neue Implementierung davon - ein Schalter (GPIO, ganz simpel), und wie man den konfiguriert.

In meinen Augen hat der seine eigene config, *nicht* DB-basiert! Gibt es mE keinen Grund zu - denn dann muss man schon wieder viel zu viel Infrastruktur fuer etwas sehr simples machen. Und man konfiguriert sich in der Weboberflaeche ja auch nicht irgendwelche physikalischen Dinge dazu!

Dann einen Server, der das Protokoll spricht schreiben, und mit dem Schalter spielen lassen. Zu Beginn muss der noch nicht mal eine DB unterstuetzen, sondern einfach mal seine angemeldeten Schalter darstellen & schaltbar machen.

Von da ab kannst du sicher vieles von deinem existierenden Code - und wenn "nur" Layout und Arbeitsfluesse - uebernehmen, damit man den eben als Terrarienlicht ueber das Jahr steuert - mit DB zum persistieren von Istzustand, wiederaufsetzen durch neustart usw.

Dann muss man sich dem Thema Robustheit widmen - wie werden die client-Prozesse gestartet? Wenn moeglich wuerde ich das ja einem externen, erprobten Tool wie zB supervisord ueberlassen. Das kann dann aber ggf. seine Konfiguration auch generiert bekommen - letztlich ist aber beim client-Server immer das Problem, dass der Server damit umgehen koennen muss, dass ein Client weg ist. Kann ja auf einem anderen PI leben, wenn der da nicht gestartet ist, ist halt essig.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

@__deets__

Bin noch mitten in meiner Antwort, da kommt schon der nächste Post ;)
Okay, erstmal zu deinem Text vom Wochenende:
das die clients in deinem Diagramm Zugriff auf die Datenbank erhalten - und nicht nur der Server - halte ich fuer uengluecklich. Das deutet eine Menge Probleme in meinen Augen: als client-Entwickler muss ich *zwei* APIs - Server & DB - bedienen, eine davon nagelt mich auch noch auf eine spezifische Datenbanktechnologie fest, und last but not least kann ich damit wahrscheinlich dieselben Dinge auf mindestens zwei, im Zweifel auf gemischet 3000 Arten erledigen. Standardisiere das auf das JSON-basierte Netzwerkprotokoll.
Hmm, für mich sah das nach weniger Arbeit aus, ansonsten müsste ich ja alles per websockets & json senden.
Ich werde aber auf jeden Fall darüber nachdenken, ist auf jeden Fall ne ganze Menge an Arbeit wenn ich das jetzt umstellen will.
die Darstellung der JSON-Formate als Tabelle ist unorthodox, und vermittelt mir ueberhaupt nicht, was du da erwartest.
Okay, ich war der Meinung das die Tabelle deutlich macht wie ich das "Protokoll" verstehe.
Ich habe leider null Erfahrung mit sowas und weiß nicht wie ich meine Gedanken am besten zu "Papier" bringe.
Zahlwerte in deutsche Schreibung mit Komma haben in JSON entweder nichts verloren, oder werden ineffizient als Strings uebertragen - kann ich mir fast nicht vorstellen.
Der Teil der Software hat mit den Sensoren zu tun und ist über 1 Jahr alt.
Wenn ich demnächst zu den Sensoren komme, werde ich mir das anschauen und dann entsprechend anpassen.
Echte JSON-Nachrichten sind da sinnvoller. Spaeter sieht man, dass du immer Arrays meinst. Halte ich fuer wenig sinnvoll - statt Reihenfolge sollte man lieber ein Objektliteral mit diversen definierten und ggf. optionalen Schluesseln waehlen.
Den letzten Satz verstehe ich nicht, kannst du mir bitte dazu ein Beispiel geben?
es gibt in Python Stringliterale mit dreifachen Anfuehrungszeichen - damit schreibt man mehrzeilige Strings einfach, statt so eine \-Verkettungsorgie zu feiern.
Was meinst du damit?
wirklich *einfach* erweiterbar ist das System laut des gezeigten Quellcodes nicht. Die grosse if-Kaskade, um Schaltertypen anzulegen, ist unschoen. Vor allem hast du an anderen Stellen standardisierte Schnittstellen, mit generisch benannten Argumenten irgendeiner Art - aber mit einem mal bekommen alle Klassen ihre ganz persoenlichen Argumente uebergeben.
Ja, das hast du leider Recht und wenn ich wüsste wie, dann würde ich es gerne ändern.
Auf die if-Kaskade verzichten und allen Klassen die gleichen Argumente übergeben wäre natürlich besser, scheitert aber wie gesagt noch an meinem (nicht vorhandenen) Können. :wink:
Wenn das standardisiert abliefe, koenntest du eine simples Factory-Pattern verwenden, und das ggf. mit einem Plugin-System kombinieren, um neue Schalter-Typen anzulegen.
Auch hier verstehe ich gerade nur Bahnhof, eventuell würde mir ein kleines Beispiel helfen.
Ob das sinnvoll ist, kann ich schwer beurteilen. Ich glaube ja, es waere zielfuehrender, ueber die entsprechende Server JSON API diverse Typen - an/aus, stufenloser regler usw - anzubieten - und das war's dann auch schon. Gar kein Grund, da grossartig Code fuer zu schreiben - ob hinter einem Schalter nun ein PI oder ein Arduino oder was auch immer steckt, muss den Server meistenteils doch gar nicht interessieren. Da sind viel eher Fragen nach Gruppierung und Automatisierung relevant.

Dafuer wandern dann deine ganzen Tinkerforge-dieses und GPIO-jenes in eigenstaendig laufende Programme, die wissen dann halt, was sie wissen muessen.
Hier bin ich noch dabei zu verstehen was du mir sagen willst, bzw. ob das für mich so umsetzbar ist.
m Quellcode habe ich property-Deklarationen gefunden, die nicht mit Dekorator gemacht wurden - das ist meiner Erinnerung schon seit Python 2.4, also ungefaehr 12 Jahren, nicht mehr so ueblich.
Verstehe ich (noch) nicht. Arbeite aber dran :)
es findet sich Code wie folgender:
...
...
Da ist viel komisch. Es faengt an mit dem komplett identischen Code fuer beide Faelle - da sollte also eine vereinheitlichte Bedingung benutzt werden, und nur einmal der Code.
Die Aufrufe "switch_socket_b" und "switch_socket_c" sind so von der API des Hersteller vorgegeben.
Würde das auch gerne in eine Zeile packen, nur wie?
Ich kann ja an der API nichts ändern.
Das pass ist ueberfluessig.
Das gehört eigentlich auch weg.
Habe ich wohl übersehen und dann vergessen zu löschen.
Die switched-Variable wird danach sofort benutzt in einem if - sie koennte also weg, und der entsprechende Code einfach eingerueckt darunter.
Das würde aber nur gehen wenn ich die API-Aufrufe irgendwie in eine Zeile bekommen würde oder wie meinst du das?
Wenn ich den Code darunter einrücke, dann betrifft das ja nur die 2. if-Abzweigung oder ich schreibe es bei beiden direkt drunter.
Dann spare ich mir die Variable "switched", habe aber doppelten Code.
Oder verstehe ich da etwas ganz falsch?
Die Konstanten sind unerklaert und magisch, die Werte (mit Leerzeichen) eher unueblich gewaehlt, die generischen Argumentnamen unklar.
Hier stehe ich gerade auf dem Schlauch. Auf was beziehst du dich, auf welche Konstanten?
Die Werte ergeben sich über die Datenbankeinträge für den jeweiligen Schalter.
Kann ja mir der einen Hardware theoretisch hunderte von Schaltern nutzen, deswegen muss ich etwas flexibel sein und kann nicht alles direkt in den Code schreiben.
Okay, das Leerzeichen hätte man weglassen können ;)
Die Argumentnamen erkläre ich eigentlich im Blog und die werden etwas klarer wenn man sich im Frontend die Beispiel-Schalter ansieht.
Hoffe ich zumindest.

Du hast dir hier mit dem "b switch" & Co aber auch ein schönes Beispiel ausgesucht, dieses Modul (TF RemoteSwitch) ist noch nicht ganz fertig.
Soll aber keine Ausrede sein.
wann ein Attribut bei dir privat oder oeffentlich ist folgt keinem mir erkennbaren Schema
Eigentlich sollen alle temporären Variabeln privat sein.
Zu Beginn habe ich alle mit einem "__var" versehen.
Irgendwann hat man mir aber gesagt das würde man nicht mehr machen.
Deswegen ist es jetzt etwas durcheinander.


Soviel schon mal zu deinem ersten Post.
Ja, ich weiß es gibt schon einen neuen.

Wie du sicherlich gemerkt hast bin ich kein Profi, im Gegenteil, ich arbeite in einer Schreinerei im Büro.
Von daher ist es schon bitter zu sehen wie wenig ich doch richtig mache.

Es würde mich freuen wenn du auf meine Frage kurz eingehen könntest.
Ich werde in den nächsten Tagen nachdenken und schauen wie ich deine Ideen eventuell umsetzen kann.

Jetzt zu deinem neuen Eintrag ...
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

So, Teil 2 :D
Also zb ein Schalter:

- hat einen Zustand (on/off)
- kann der Zustand gemeldet werden? Oder muss man den definiert setzen, und selbst nachhalten, wie er steht?
- hat einen Namen, Beschreibung, eindeutige ID?
- kann geschaltet werden
- kann (wenn Frage oben nach der Rueckmeldung mit ja beantwortet wurde) automatisch "kippen"? (das koennte zb ein Notaus einer Pumpe mit Ueberlastungsschutz oder so etwas sein)
- kann Fehler als Text/Code melden?
Damit habe ich mich auch schon befasst.
Das Problem ist das man Schalter und Schalter-Typ unterscheiden muss, bzw. es noch einmal diese Unterscheidung gibt.
Breche ich es auf den Schalter runter dann hat jeder Schalter:
- Zustand off/on (Werte einstellen wäre ein Regler)
- kann Rückmeldung geben, ansonsten -99 als Fehlercode
- wird über datenbank id identifiziert
- kann geschaltet werden
- ist einem client zugewissen, hat namen, beschreibung

Soweit mir möglich habe ich auch versucht das in der Klasse/Datenbank entsprechend abzubilden.

Du würdest, wenn ich dich richtig verstanden habe , alles in eine Klasse packen und dir die Daten aus der Datenbank vom server per json schicken lassen, richtig?
Kann ich da dann weiter websockets einsetzen und mein json-"Protokoll" entsprechend aufbohren?
Wie meldet der Schalter sich beim Server an, was wird zyklisch und ereignisbasiert ausgetauscht usw.
Von sich aus machen die Schalter gar nix, melden sich also nicht am Server an.
Deshalb gehe ich ja den anderen Weg und lasse die Schalter vom Nutzer anlegen.
Dadurch das in der Datenbank ein entsprechender Eintrag für den client vorhanden ist wird der Schalter dann initialisiert.

Du denkst eher daran das der Client beim starten die angeschlossene Hardware überprüft und dann alle gefundenen Schalter an den Server meldet, richtig?
Dann könnte ich aber das System nur richtig über das frontend nutzen wenn alle Clients laufen, ansonsten würden ja Schalter fehlen.
Wenn man sich das ueberlegt hat (zumindest im groben), baut man eine neue Implementierung davon - ein Schalter (GPIO, ganz simpel), und wie man den konfiguriert.
Mit deinen Denkanstössen bringst du mich derzeit gewaltig zum überlegen ...
In meinen Augen hat der seine eigene config, *nicht* DB-basiert! Gibt es mE keinen Grund zu - denn dann muss man schon wieder viel zu viel Infrastruktur fuer etwas sehr simples machen. Und man konfiguriert sich in der Weboberflaeche ja auch nicht irgendwelche physikalischen Dinge dazu!

Dann einen Server, der das Protokoll spricht schreiben, und mit dem Schalter spielen lassen. Zu Beginn muss der noch nicht mal eine DB unterstuetzen, sondern einfach mal seine angemeldeten Schalter darstellen & schaltbar machen.

Von da ab kannst du sicher vieles von deinem existierenden Code - und wenn "nur" Layout und Arbeitsfluesse - uebernehmen, damit man den eben als Terrarienlicht ueber das Jahr steuert - mit DB zum persistieren von Istzustand, wiederaufsetzen durch neustart usw.
Hier kann ich dir noch nicht ganz vorstellen?
Verstehe ehrlich gesagt nicht wirklich was du da vorhast?
Dann muss man sich dem Thema Robustheit widmen - wie werden die client-Prozesse gestartet? Wenn moeglich wuerde ich das ja einem externen, erprobten Tool wie zB supervisord ueberlassen. Das kann dann aber ggf. seine Konfiguration auch generiert bekommen -
Mache ich mit einem init-Skript und dann wartet der client bis er sich mit dem server verbinden kann.
etztlich ist aber beim client-Server immer das Problem, dass der Server damit umgehen koennen muss, dass ein Client weg ist. Kann ja auf einem anderen PI leben, wenn der da nicht gestartet ist, ist halt essig.
Das ist dem server im Moment ziemlich egal.
Wenn ein client weg ist, dann gibt es einen Eintrag ins Log und die Befehle laufen ins leere, da es keine Rückmeldung gibt könnte man das dann auch sehen - muss ich aber noch in die Weboberfläche einbauen.
Wenn der client wieder da ist, dann meldet er sich am server an und alles ist gut.
Zumindest lief es in Tests bisher so.

Hmm, ich denke ich habe ein wenig mehr verstanden was du meinst.
Leider noch nicht komplett und leider fehlen mir (im Moment) noch die Fähigkeiten die neuen Ideen auch umzusetzen.

Trotzdem schon mal ganz herzlich: Dankeschön
BlackJack

@der_Mausbiber: Beispiel für Objektliteral statt Array:

Nicht:

Code: Alles auswählen

['switch_turn', '192.168.128.66', 8, 'on']
Sondern:

Code: Alles auswählen

{
  "usage": "switch_turn",
  "ip": "192.168.128.66",
  "id": 8,
  "value": "on"
}
Dann ist beim lesen der Nachrichten klar was welcher Wert bedeutet und man kann auch einfach Nachrichten mit mehr oder weniger Elementen verwenden, oder später das Protokoll ändern in dem man Elemente hinzufügt oder weg lässt, ohne dass dadurch überall Code mit magischen Indexwerten angepasst werden muss.

Python kennt mehrzeilige Zeichenkettenliterale:

Code: Alles auswählen

"""abc
def"""

# <=>

"abc\n" \
"def"
Ad `property()`: Das benutzt man als Dekorator und nicht als Funktionsaufruf. Wie das aussieht sieht man in der Dokumentation zu `property()`.

Was meinst Du mit temporären privaten Variablen? Temporäre Variablen sind ja sowieso nur lokale Variablen und die sind von Natur aus ”privat”, weil die nur innerhalb der Funktion oder Methode sichtbar sind.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

@BlackJack

Danke, jetzt verstehe ich was mit Objektliteral anstatt Array gemeint ist und weiß das ich das noch diese Woche ändern werde.
Kann alles was du sagst nur zustimmen.
Wieder was wichtiges gelernt.

Das gleiche gilt für mehrzeilige Zeichenkettenliterale, auch das werde ich diese Woche ändern.

Den Rest muß ich mir morgen mal ansehen.

Trotzdem hat mir das schon mal weiter geholfen.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nur eine kurze Zwischeantwort: bezueglich des komischen Codes:

Code: Alles auswählen

  if arg_b == "b switch":
            self.bricklet.switch_socket_b(int(arg_c), int(arg_d), switch_to)
            switched = True
            pass
        elif arg_b == "c switch":
            self.bricklet.switch_socket_c(int(arg_c), int(arg_d), switch_to)
            switched = True
            pass
            
        if switched:
            # tu was
Die offensichtliche Art, das zu verbessern ist doch einfach nur

Code: Alles auswählen

   if arg_b in ("b switch", "c switch"):
            self.bricklet.switch_socket_b(int(arg_c), int(arg_d), switch_to)
            # tu was
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: offensichtlich gibt es unterschiedliche Funktionsaufrufe, wobei ich nicht geschaut habe, ob switch_socket_b und switch_socket_c eigentlich das selbe tun und es nur ein switch_socket(arg_b, ...) geben sollte. Schlimmer ist noch, dass es überhaupt so unaussagekräftige Namen wie arg_a/b/c gibt.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

offensichtlich gibt es unterschiedliche Funktionsaufrufe, wobei ich nicht geschaut habe, ob switch_socket_b und switch_socket_c eigentlich das selbe tun und es nur ein switch_socket(arg_b, ...) geben sollte.
Nochmal: Darauf habe ich keinen Einfluß!

Das RemoteSwitch-Bricklet kann 3 x verschiedene Funk-Protokolle ansprechen.
Deshalb gibt es von seitens des Herstellers auch 3 x Funktionsaufrufe:
- switch_socket_a
- switch_socket_b
- switch_socket_c

Und nein, die tun nicht das selbe, also im Prinzip schon, aber jede Funktion steht für ein anderes Protokoll.

Natürlich wäre es schöner wenn es nur einen Aufruf geben würde "switch_socket" und man das Protokoll als Argument übergeben könnte.
Dem ist aber leider nicht so und ich habe auch keinerlei Einfluß darauf.
Ich muss hier mit der API leben die der Hersteller liefert.

Deswegen wird
Die offensichtliche Art, das zu verbessern ist doch einfach nur

Code: Alles auswählen

   if arg_b in ("b switch", "c switch"):
            self.bricklet.switch_socket_b(int(arg_c), int(arg_d), switch_to)
            # tu was
auch nicht funktionieren, weil in diesem Beispiel schaltet er nur Protokoll "B", die anderen beiden fallen dabei raus.
Oder sehe ich das falsch?

Schlimmer ist noch, dass es überhaupt so unaussagekräftige Namen wie arg_a/b/c gibt.
Da fällt mir keine bessere Lösung für ein, da jeder Schalter-Typ (Relais, GPIO, Funk, USB-Relais) andere Argumente benötigt musste ich einen Kompromis finden.
Z.Bsp. braucht ich bei GPIO-Pins nur arg_A zum abspeichern des Pins, bei Tinkerforge Relais brauche ich arg_a zum speichern der Modul UID und arg_b um das Relais anzugeben (1-4) und zu guter letzt brauche ich beim Tinkerforge RemoteSwitch alle 4 Argumenten, in arg_a steht dann die UID, in arg_b das Funk-Protokoll und in arg_c + arg_d der eigentliche Funk-Code.

Vermeiden könnte ich das meiner Meinung nach nur, wenn ich für jeden Typ von Schalter eine extra Datenbank anlegen würde.

Oder stehe ich komplett auf dem Schlauch ?


PS: Freut mich aber das gerade Ihr 3 x euren Send dazu abgebt, mit Sirius3 und BlackJack hatte ich hier schon zu tun, und ich habe sehr gute Erfahrungen mit euch gemacht.
Ihr seid freundlich und vorallem ihr seid gut.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sirius3 hat geschrieben:@__deets__: offensichtlich gibt es unterschiedliche Funktionsaufrufe, wobei ich nicht geschaut habe, ob switch_socket_b und switch_socket_c eigentlich das selbe tun und es nur ein switch_socket(arg_b, ...) geben sollte. Schlimmer ist noch, dass es überhaupt so unaussagekräftige Namen wie arg_a/b/c gibt.
Hah! Verzeihung - das habe ich tatsaechlich komplett ueberesehen, dass da ein Unteschied in dem Suffix war. Was dafuer spricht, dass es ganz definitiv anders heissen sollte - so ist das zu subtil... (Ja, ich weiss, ist der Hersteller. Der kann ja auch Mist machen :) )

Eine Alternative - unabhaengig vom Namen - waere dann natuerlich, die Funktion nachzuschlagen:

Code: Alles auswählen

func = {
    "b switch" : self.bricklet.switch_socket_b,
    "a switch" : self.bricklet.switch_socket_a,
}.get(arg_b)
if func is not None:
    func(....)
    # tu was
Zuletzt geändert von __deets__ am Dienstag 23. Februar 2016, 14:50, insgesamt 1-mal geändert.
BlackJack

@__deets__: Psst, es wird wieder nur eine Switch-Funktion in beiden Fällen aufgerufen. ;-)
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da fällt mir keine bessere Lösung für ein, da jeder Schalter-Typ (Relais, GPIO, Funk, USB-Relais) andere Argumente benötigt musste ich einen Kompromis finden.
Z.Bsp. braucht ich bei GPIO-Pins nur arg_A zum abspeichern des Pins, bei Tinkerforge Relais brauche ich arg_a zum speichern der Modul UID und arg_b um das Relais anzugeben (1-4) und zu guter letzt brauche ich beim Tinkerforge RemoteSwitch alle 4 Argumenten, in arg_a steht dann die UID, in arg_b das Funk-Protokoll und in arg_c + arg_d der eigentliche Funk-Code.

Vermeiden könnte ich das meiner Meinung nach nur, wenn ich für jeden Typ von Schalter eine extra Datenbank anlegen würde.

Oder stehe ich komplett auf dem Schlauch ?
In meinen Augen ist das genau der Fall, den ich weiter oben schon skizziert habe: was interessiert es denn deinen Server, wie genau der Switch anzusprechene ist? Das hat weder da, noch in einer Datenbank etwas verloren. Eine Datenbank dient dazu, Dinge, die sich (relativ oft) aendern koennen zu speichern.

Deine Hardware-Konfiguration aendert sich aber nicht (jedenfalls nicht oft). Stattdessen baust du einen neuen Schalter irgendwozu ein, und dann brauchst du den genau einmal anzumelden. Ich wuerde das mit einer Konfigurationsdatei machen.

Dann meldet sich eben der Schalter beim Server an - und kann geschaltet werden. Das ist ja immer dasselbe: der Server schickt dem Schalter "geh an", "geh aus", und der Schalter macht das, und meldet zurueck "bin an", "bin aus", oder "bin kaputt".

Ein bisschen Pseudocode fuer einen Schalter-Client:

Code: Alles auswählen


from smartHome import SwitchBase


class GPIOSwitch(SwitchBase):

     CONFIG = SwitchBase.mergeConfigs(
            SwitchBase.CONFIG,
            {
                "GPIO_NO" : int, # erklaert den Wert GPIO_NO als int, der in der Konfig stehen muss
            }
     )

    def __init__(self, config):
         super(...).__init__(config)
         self._gpio = config["GPIO_NO"]
         self._setup_gpio(self._gpio)
   
   
    def set_switch(self, on):
          self._set_gpio(on)  
          # aus Basisklasse, GPIO switch kann
          # nie einen Fehler haben -also einfach
          # den soll-Zustand schicken
          self.send_switch_state(on)

Eine Konfiguration sieht dann so aus:

Code: Alles auswählen

SMART_HOME_URL=http://localhost:1234/
NAME=Terrarium Licht
GPIO_NO=10
Und nichts und niemand muss etwas in eine DB eintragen.

Wenn du zb einen Pumpenschalter haben willst, der mit Ueberlastungsschutz arbeitet, dann saehe das ggf. so aus:

Code: Alles auswählen


from smartHome import SwitchBase


class PumpSwitch(GPIOSwitch]):

     CONFIG = SwitchBase.mergeConfigs(
            GPIOSwitch.CONFIG,
            {
                "OVERHEATED_GPIO" : int, # erklaert den Wert OVERHEATED_GPIO als int, der in der Konfig stehen muss
            }
     )

    def __init__(self, config):
         super(PumpSwitch, self).__init__(config)
         self._overheat_gpio = config["GPIO_NO"]
         self._setup_overheat_gpio(self._overheat_gpio)
         self._overheated  = False

          
   
    def set_switch(self, on):
          if not self._overheated:
              super(PumpSwitch, self).set_switch(on)
          else:
               self.send_switch_state(False)

Was da natuerlich noch fehlt ist, dass der PumpSwitch den GPIO ueberwacht, und dann self._overheated setzt, und natuerlich auch sofort meldet, dass er "aus" ist.

Ich glaube ein grosses "Problem" ist, dass du mit einer nicht-client-Server Anwendung angefangen hast, und mit einem System zum zurechtkonfigurieren von GPIO-basierten Schaltaufgaben, die du in eine DB gespeichert hast. Dann erweitert um mehr - und nun soll es client-server werden, aber der ganze Code ist durchzogen mit den Annahmen ueber all die Schalter, die du unterstuetz.

Aber das ist IMHO nicht sinnvoll, wenn du client Server hast. Mit diesem Ansatz hier kannst du sogar ggf. einen Arudino mit Ethernet-Shield anklemmen, weil der nur JSON-Pakete bekommt und schickt. Versuch dem mal, MySQL beizubringen...
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Warum auch immer der dann wieder alles hier reingequotet hat...

[EDIT] Danke, Blackjack... .
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Zwischenbericht:
Ich habe die Antworten nicht vergessen, nur komme ich seit ein paar Tagen zu nix mehr.
Ich mache mir aber weiter Gedanken zu den Ausführungen & Ideen hier, und werde erst Antworten wenn ich das Ganze komplett verstanden habe.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

So, ich versuche immer noch deine Idee und den Pseudo-Code vollständig zu verstehen.

Ich kann mir schon vorstellen das System dahingehend umzustellen, den so wie es ist, ist es nicht wirklich gut.
Ich glaube ein grosses "Problem" ist, dass du mit einer nicht-client-Server Anwendung angefangen hast, und mit einem System zum zurechtkonfigurieren von GPIO-basierten Schaltaufgaben, die du in eine DB gespeichert hast. Dann erweitert um mehr - und nun soll es client-server werden, aber der ganze Code ist durchzogen mit den Annahmen ueber all die Schalter, die du unterstuetz.
Nein, ehrlich gesagt hat das Ganze direkt als Client/Server-System angefangen - allerdings war damals alles direkt in den "Code geschrieben".
Das System war da nur für mich gedacht und komplett ohne Datenbank und Einstellungsmöglichkeiten.

Mit der Erweiterung es für andere benutzbar zu machen kamen dann die Probleme ...

Einen Teil eurer Ideen habe ich bereits umgesetzt, aktuell bin ich wie oben erwähnt wieder dabei deine Idee komplett zu verstehen und auf mein System zu übertragen.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

So, jetzt bin ich soweit.

Eins gleich vorweg, die Datenbank-Anbindung im Client werde ich entfernen, in Zukunft läuft das über den Server.
An diesem meldet sich der Client an, schickt seine IP und bekommt im Gegenzug seine Daten geschickt.

Dazu hätte ich allerdings auch eine Frage.
Wie kann der websocket-Server eine Nachricht nur an einen spezifischen Client senden?
Im Moment sendet der websocket-Server ja immer an alle verbundenen Clients.

Gut, kommen wir zu deiner Idee und ob ich sie richtig verstanden habe:

Bei deiner Version verzichte ich quasi auf die Datenbank-Einträge zu den Schalter.
Die Konfiguration für jeden einzelnen Schalter wird dann über eine config-Datei auf dem jeweiligen Client geregelt.
Beim Start des Clients lädt dieser die config-Dateien und "weiß" damit welche Schalter angeschlossen sind.
Diese Schalter meldet der Client dann dem Server.
Soweit habe ich doch alles richtig verstanden?

Jetzt bin ich mir nicht mehr sicher wie es weiter gehen soll?
Um mit den Schalter in html-Frontend arbeiten zu können muss ich diese irgendwie mit der Datenbank verbinden.
Dafür bräuchte ich im Prinzip id's und damit das Ganze auch nach einem Neustart funktioniert müsste ich am besten in den config-Dateien bei jedem Schalter eine ID angeben, oder gibt es da einen anderen Weg?
Die nächste Frage die sich mir stellt, wenn der Client offline ist, kann ich dann überhaupt im html-frontend mit dem Schalter "arbeiten"?
Im Moment kann ich im frontend rumspielen wie ich will, egal ob die Schalter wirklich vorhanden sind oder nicht.

Bin ich noch auf dem richtigen Weg oder geht deine Idee hier in eine ganz andere Richtung?

Zu guter letzt stellt sich mir die Frage ob deine Idee wirklich einfacher und übersichtlicher ist.
Dazu merke ich an, das meine jetzige Lösung definitiv noch einer Änderung bedarf.
Ich habe bei deiner Idee ein Problem mit den config-Files bei vielen Schalter.

Ein Tinkerforge Relais wird über eine gemeinsame Methode angesprochen, bietet aber bis zu 4 x Schalter - die ich komplett verschieden belegen kann.
Dazu kommt, das ich 3-4 x dieser Module an einem Client betreiben kann, dazu auch noch USB-Relais von anderen Hersteller, wieder mit 2 x Schalter an einem Gerät.
Da können schnell 10-20 x Schalter an einem Client hängen, verschiedene Module, verschiedene Konfigurationen.
Das dann nochmal bei jedem Client (sofern mehrere vorhanden sind), hmm, ich weiß nicht ob das nicht schnell unübersichtlich wird.

Wobei ich deine Idee vom Grundsatz ja nicht schlecht finde.
Ich fürchte halt nur das es bei größeren Setups schnell sehr unübersichtlich wird.

Es sei den ich habe dich ganz falsch verstanden.

Ich würde mich freuen wenn dazu eine kurze Rückmeldung kommen würde.
Danke im Voraus
Antworten