smartHome Zeitschaltuhr 2.0

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
__deets__
User
Beiträge: 14522
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: 14522
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
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Zu dem Ganzen hätte ich noch eine Frage:

Ist es möglich den den Namen einer Klasse bei deren Initialisierung durch eine Variabel zu ersetzen?

quasi anstatt

Code: Alles auswählen

switches[switches_info[result['switches_id'], "index"]] = BrickletQuadRelay(result['argA'],
                                                                                        result['switches_id'],
                                                                                        DEVICE_IP,
                                                                                        tinkerforge_connection, logger,
                                                                                        consumers)
etwas in dieser Richtung

Code: Alles auswählen

klassenname = result['class']
switches[switches_info[result['switches_id'], "index"]] = klassenname(result['argA'],
                                                                                        result['switches_id'],
                                                                                        DEVICE_IP,
                                                                                        tinkerforge_connection, logger,
                                                                                        consumers)
Dann könnte man die Schalterkonfiguration schon etwas vereinfachen.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

der_Mausbiber hat geschrieben:Ist es möglich den den Namen einer Klasse bei deren Initialisierung durch eine Variabel zu ersetzen?
Ja.
the more they change the more they stay the same
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

:wink:

Könntest du mir ganz kurz erklären wie?
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

dafür nimmt man ein Wörterbuch:

Code: Alles auswählen

NAME2SWITCHCLASS = {
    "BrickletQuadRelay": BrickletQuadRelay,
    ...
}

switchclass = NAME2SWITCHCLASS[klassenname]
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Okay, das klingt einfach.

Kann ich das Wörterbuch auch aus einer Datenbank generieren oder muss ich die Daten im Wörterbuch manuell eingeben?
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@der_Mausbiber: im Wörterbuch stehen die Pythonklassen, die Du ja instanziieren willst. Das geht nicht über eine Datenbank.
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Hmm, okay, dann kann ich den Code damit zwar vereinfachen, ein manueller Eingriff ist aber immer noch notwendig.
Schade.

Ich hoffe das "__deets__" nochmal kurz Zeit findet und mir sagen kann ob ich seine Idee richtig verstanden habe.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hallo,

zuerst einmal klingt das so, als ob du meine Grundidee verstanden hast. Zu deinen Fragen ein paar Antworten, und weil ja (voellig zu Recht) auf erweiterte Schalter hinweist, versuche ich auch da etwas zu zu sagen.

Zuerst einmal ist es prinzipiell ja kein Problem, meinen Ansatz auf mehrere Schalter zu generalisieren. Ein Client meldet dann eben immer eine ganze Reihe von Schaltern an, und ein Schalter ist damit auch abgedeckt. Jeder Schalter bekommt einen Namen, und der muss eindeutig sein. Und diese Forderung ist auch denke ich simpel genug zu erfuellen fuer die Benutzer. Wer dabei einen Fehler macht, macht das auch anderweitig.

Und genau dieser Name ist auch die ID. Sobald ein neuer Schalter vom einem der Clients (oder einem neuen) gemeldet wird, steht der zur Verfuegung. Vorher kann man da ja eh nicht wirklich etwas mit anfangen, ich denke mal nicht, dass Leute sich komplexe Schaltszenarien ausdenken und einpflegen, bevor sie einen Schalter wirklich verbunden und konfiguriert haben.

Bestenfalls braucht die Oberflaeche dann einen Weg, einen bekannten Schalter rauszuwerfen, wenn man weiss, dass der nicht mehr existiert.

Die Frage, ob die Konfiguration uebersichtlich ist oder nicht ist schwieriger zu beantworten. Zuerst einmal sollte man sich vergegenwaertigen, dass die Einrichtung ein seltenes Ereignis ist. Wenn ich also nach der "Pareto-Regel" vorgehe, und versuche mit so wenig Aufwand wie moeglich so viel Ergebnis wie moeglich zu erzielen, wuerde ich immer den Wert eher auf die leichte Bedienbarkeit eines konfigurierten Systems legen, statt die Konfiguration so einfach wie moeglich zu machen.

Dann ist natuerlich das entwanzen eines komplexen, interagierenden Systems wie deiner Client-Server-Anwendung schwieriger, als eine Komponente durchzutesten, bis man sich sicher ist, dass sie funktioniert.

Ich wuerde zB den Client immer so bauen, dass er die Konfiguration nimmt, und auch gleich eine Test-Schaltung vornehmen kann, also so etwas:

Code: Alles auswählen

$ gpio-client --config config.json --test-mode cyclic-toggle --test-switch garagenlicht
Dann kannst du dort pruefen, ob alles geht, ohne dass du den Server ueberhaupt bemuehen musst.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich sehe jetzt erst, das in der Zwischenzeit noch Nachrichten reingekommen sind.

Woher soll denn die Datenbank fuer die Klassennzuordnung kommen? Das waere ja wieder Spezialcode fuer die Schalter im Server, der davon gar nichts wissen soll. Der kennt nur Schaltername und Zustand, und die Zeitablaufssteuerung. Oder eine DB-Verbindung im Client, die nicht noetig ist :)

Auf *clientseite* kannst du das natuerlich ggf. machen mit dem Woerterbuch (handgeschrieben), ich weiss aber nicht, ob es nicht sinnvoller ist, fuer jeweils eine Sorte Schalter einen Client zu schreiben. Ob man jetzt einen oder drei Prozesse mit Clients hat macht keinen grossen Unterschied, aber die Komplexitaet im Code scheint mir im Moment eher dein Problem - loes die Dinge einzeln, und dann laesst sich auch besser sehen, was verallgemeinerbar ist und was nicht.

Was mir auch noch einfaellt: ich kenne das Websocket-Modul nicht. Aber ich seher auch ersteinmal kein Problem darin, das jeder Client die Schaltnachrichten der anderen bekommt. Das macht die Programmierung einfacher, und die Last an Schaltnachrichten ist ja sehr gering - aus Sicht eines Computers dauert es Aeonen zwischen den Schaltvorgaengen, wenn wir Menschen schon mit einem epileptischen Anfall am Boden liegen. Da muss man nicht optimieren, solange alles im selben Netzwerk haengt.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

So, hier mal eine kleine Skizze:

Code: Alles auswählen

from __future__ import print_function

import sys
import json
import argparse
import time


def error(message, code=1):
    sys.stderr.write(message)
    sys.stderr.write("\n")
    sys.exit(code)


class Switch(object):

    def __init__(self, config, on_off_function):
        self._name = config["name"]
        self._inverted = config["invert"]
        self._on = False
        self._on_off_function = on_off_function
        self.set(False)


    def set(self, state):
        self._on = state
        real_state = state ^ self._inverted
        self._on_off_function(real_state)



class SwitchClientBase(object):

    def __init__(self):
        self._switches = {}


    def add_specific_arguments(self, parser):
        """
        Overload this to add specific arguments for your switch implementation
        """
        pass


    def run(self):
        parser = argparse.ArgumentParser()
        parser.add_argument("--config", help="configuration file")
        parser.add_argument("--print-basic-config", action="store_true")
        parser.add_argument("--test-mode", choices=["cyclic", "on", "off"])
        self.add_specific_arguments(parser)

        opts = parser.parse_args()
        if opts.print_basic_config:
            print("A basic configuration. All keys with `_description` are just explanations and can be left out")
            print(json.dumps(self.basic_config(), indent=4))
            sys.exit(0)

        if not opts.config:
            error("You need to give a config-file. Get an example using the `--print-basic-config` argument")

        with open(opts.config) as inf:
            config = json.load(inf)


        for switch in config["switches"]:
            self._switches[switch["name"]] = self._setup_switch(switch)


        if opts.test_mode is not None:
            getattr(self, "test_%s" % opts.test_mode)(opts)


    def test_cyclic(self, opts):
        state = True
        while True:
            time.sleep(1.0)
            for switch in self._switches.values():
                switch.set(state)
            state = not state


    def basic_config(self):
        return {
            "uri" : "server URI such as tcp://localhost:12345",
            "switches" : [
                {
                    "name": "unique switch name, e.g garage",
                    "invert" : False,
                    "invert_description" : "If True, the switch state is inverted, meaning a ON switch will deliver a LOW-level",
                },
                {
                    "name": "unique switch name, e.g terrarium",
                    "invert" : True,
                    "invert_description" : "If True, the switch state is inverted, meaning a ON switch will deliver a LOW-level",
                },
            ],
        }


    def _setup_switch(self, config):
        on_off_function = self.on_off_function(config)
        return Switch(config, on_off_function)



    def on_off_function(self, config):
        raise NotImplementedError



class GPIOClient(SwitchClientBase):


    def basic_config(self):
        config = super(GPIOClient, self).basic_config()
        # augment with our specific info
        for pin, switch in enumerate(config["switches"]):
            switch["gpio_pin"] = pin
            switch["gpio_pin_description"] = "Number of the GPIO-pin to toggle for that switch, in Broadcom-Numbering"
        return config


    def on_off_function(self, config):
        # in real, we would set up the GPIO configured here
        pin = config["gpio_pin"]
        def on_off_function(state):
            print("GPIO", pin, state)

        return on_off_function



def main():
    client = GPIOClient()
    client.run()


if __name__ == '__main__':
    main()
Mit Konfiguration:

Code: Alles auswählen

{
    "switches": [
        {
            "invert": false,
            "name": "garage",
            "gpio_pin": 0
        },
        {
            "invert": true,
            "name": "terrarium",
            "gpio_pin": 1
        }
    ],
    "uri": "tcp://localhost:12345"
}
Aufgerufen mit

Code: Alles auswählen

$ python /tmp/test-gpio-client.py --config /tmp/test-config.json --test-mode cyclic
bekomme ich dann halt Ausgaben im Sekundenrhytmus.

Achtung, Server-Verbindung oder andere Testmodi sind noch nicht implementiert!
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

So, jetzt ein Server:

Code: Alles auswählen

from __future__ import print_function

import sys
import nanomsg
import json
import threading
import time, random
from functools import partial

REGISTERED_SWITCHES = {}


def random_switching(socket):
    while True:
        time.sleep(1.0)
        if REGISTERED_SWITCHES:
            name = random.choice(REGISTERED_SWITCHES.keys())
            guid = REGISTERED_SWITCHES[name]
            state = random.random() > .5
            message = json.dumps(
                {
                    "switches" : [ { "name" : name, "state" : state }],
                    "guid": guid,
                }
                )
            print(message)
            socket.send(message)


def register(message):
    guid = message["guid"]
    name = message["name"]
    REGISTERED_SWITCHES[name] = guid


def main():
    socket = nanomsg.Socket(nanomsg.BUS)
    socket.bind("ipc:///tmp/test")

    t = threading.Thread(target=partial(random_switching, socket))
    t.daemon = True
    t.start()


    while True:
        message = json.loads(socket.recv())
        print(message)
        register(message)



if __name__ == '__main__':
    main()
Ich benutze nanomsg, weil ich das gut kenne - das sollte aber auch mit Websockets klappen, das nanomsg.BUS-Protokoll hat naemlich dieselbe Eigenschaft die du beschreibst: es sind immer broadcast-Nachrichten an alle angeschlossenen Clients. Darum mache ich die durch die GUID eindeutig - obwohl das strenggenommen gar nicht notwendig waere, denn die namen muessen das ja auch sein.

Und hier der neue Client, den man auch mehrfach starten kann mit unterschiedlichen Konfigurationen:

Code: Alles auswählen

from __future__ import print_function

import sys
import json
import argparse
import time
import uuid
import nanomsg


def error(message, code=1):
    sys.stderr.write(message)
    sys.stderr.write("\n")
    sys.exit(code)


class Switch(object):

    def __init__(self, config, on_off_function):
        self._name = config["name"]
        self._inverted = config["invert"]
        self._on = False
        self._on_off_function = on_off_function
        self.set(False)


    def set(self, state):
        self._on = state
        real_state = state ^ self._inverted
        self._on_off_function(real_state)



class SwitchClientBase(object):

    def __init__(self):
        self._switches = {}
        self._socket = None
        self._guid = str(uuid.uuid4())


    def add_specific_arguments(self, parser):
        """
        Overload this to add specific arguments for your switch implementation
        """
        pass


    def run(self):
        parser = argparse.ArgumentParser()
        parser.add_argument("--config", help="configuration file")
        parser.add_argument("--print-basic-config", action="store_true")
        parser.add_argument("--test-mode", choices=["cyclic", "on", "off"])
        self.add_specific_arguments(parser)

        opts = parser.parse_args()
        if opts.print_basic_config:
            print("A basic configuration. All keys with `_description` are just explanations and can be left out")
            print(json.dumps(self.basic_config(), indent=4))
            sys.exit(0)

        if not opts.config:
            error("You need to give a config-file. Get an example using the `--print-basic-config` argument")

        with open(opts.config) as inf:
            config = json.load(inf)


        for switch in config["switches"]:
            self._switches[switch["name"]] = self._setup_switch(switch)


        if opts.test_mode is not None:
            getattr(self, "test_%s" % opts.test_mode)(opts)
        else:
            self._socket = nanomsg.Socket(nanomsg.BUS)
            self._socket.connect(config["uri"])
            self._register_switches()
            while True:
                message = json.loads(self._socket.recv())
                if message["guid"] == self._guid:
                    self._dispatch(message)


    def _dispatch(self, message):
        if "switches" in message:
            for switch in message["switches"]:
                self._switches[switch["name"]].set(switch["state"])


    def _register_switches(self):
        for name in self._switches:
            self._socket.send(
                json.dumps(
                    { "guid" : self._guid, "name" : name}
                )
            )


    def test_cyclic(self, opts):
        state = True
        while True:
            time.sleep(1.0)
            for switch in self._switches.values():
                switch.set(state)
            state = not state


    def basic_config(self):
        return {
            "uri" : "server URI such as tcp://localhost:12345",
            "switches" : [
                {
                    "name": "unique switch name, e.g garage",
                    "invert" : False,
                    "invert_description" : "If True, the switch state is inverted, meaning a ON switch will deliver a LOW-level",
                },
                {
                    "name": "unique switch name, e.g terrarium",
                    "invert" : True,
                    "invert_description" : "If True, the switch state is inverted, meaning a ON switch will deliver a LOW-level",
                },
            ],
        }


    def _setup_switch(self, config):
        on_off_function = self.on_off_function(config)
        return Switch(config, on_off_function)



    def on_off_function(self, config):
        raise NotImplementedError



class GPIOClient(SwitchClientBase):


    def basic_config(self):
        config = super(GPIOClient, self).basic_config()
        # augment with our specific info
        for pin, switch in enumerate(config["switches"]):
            switch["gpio_pin"] = pin
            switch["gpio_pin_description"] = "Number of the GPIO-pin to toggle for that switch, in Broadcom-Numbering"
        return config


    def on_off_function(self, config):
        # in real, we would set up the GPIO configured here
        pin = config["gpio_pin"]
        name = config["name"]
        def on_off_function(state):
            print("GPIO", name, pin, state)

        return on_off_function



def main():
    client = GPIOClient()
    client.run()


if __name__ == '__main__':
    main()
der_Mausbiber
User
Beiträge: 72
Registriert: Donnerstag 2. Oktober 2014, 09:51

Vielen Dank für die ausführliche Antwort.
Ich muss den Stoff jetzt erstmal in Ruhe durchgehen ... :)
Antworten