Ankündigung

Einklappen
Keine Ankündigung bisher.

KNX Werte via ArtNet-Plugin an OLA-Server senden

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    KNX Werte via ArtNet-Plugin an OLA-Server senden

    Hallo allerseits,

    ich stehe gerade etwas auf dem Schlauch und benötige einen Denkanstoss. Folgendes Problem: Ich habe in meinem Netzwerk einen Raspberry basierenden OLA-Server mit dem ich via ArtNet auf meine DMX Installation zugreifen kann. Diese besteht im wesentlichen aus einer Anzahl von eldoLED Treibern.

    Als KNX - ArtNet Gateway möchte ich smarthome.py mit dem ArtNet-Plugin verwenden. Dabei ist mir die Vorgehensweise noch nicht ganz klar. Bei dem oft verwendeten DMX-Plugin kann man ja direkt in der items.conf einen KNX Wert auf einen DMX Kanal mappen. Eine Logik ist meines Wissens dazu nicht notwendig.
    Code:
    [rgb]
        [[red]]
            type = num
            dmx_ch = 2
            knx_dpt = 5
            knx_listen =
    Doch wie mache ich das bei dem Artnet-Plugin? Dort bleibt doch nur der Weg über eine Logik? In der Logik müsste ich dann den Zustand der KNX Items abfragen und das entsprechende ArtNet Telegramm senden. Doch wie erfolgt dann die Ausführung der Logik? Ein fest vorgegebener Intervall scheint mir ungünstig. Ich möchte ja direkt reagieren sobald ein KNX Telegramm eintrifft. Die Alternative wäre eine ereignisgesteuerte Ausführung, dazu müsste ich jedoch dem Parameter "watch_item" alle relevanten Items zuordnen. Das könnten schon eine ganze Menge werden.

    Gibt es da noch eine elegantere Lösung? Am liebsten wäre mir eine Variante ohne Logik.

    Gruß Jörg

    #2
    Hallo,

    ja, leider bleibt momentan nur der Weg über eine Logik. watch_item kann aber auch mit Platzhaltern umgehen (*).
    Alternativ könnte man/Du das Plugin erweitern, damit es auf Item-Änderungen reagiert. Sollte nicht so schwer sein.

    Bis bald

    Marcus

    Kommentar


      #3
      Hi Jörg,

      ich triggere generische Logiken meist über zusätzliche Attribute:

      logic.conf
      Code:
      watch_item = *:trigger_ArtNet
      Am Item:
      Code:
      trigger_ArtNet = true
      Gruß, Waldemar
      OpenKNX www.openknx.de

      Kommentar


        #4
        Vielen Dank für Eure Tipps. Ich werde mir das mal anschauen. Das mit den Platzhaltern klingt auf jeden Fall praktikabel.

        Gruß Jörg

        Kommentar


          #5
          Hallo Jörg,

          darf ich fragen, ob du das Steuern nun mit den Logiken gelöst hast? Wie machst du das mit dem Dimmen?

          Ich möchte auch das Artnet-Plugin verwenden, um über einen AVR DMX ArtnetNode einen DMX4ALL anzusprechen. Momentan scheitere ich aber schon daran, dass ich über sh.dmx1(1,100) noch nicht den ersten Kanal zum Leuchten bringen kann.

          Grüße
          Gerd

          Kommentar


            #6
            Hallo Gerd,

            wie eingangs schon erwähnt habe ich bei mir einen OLA-Server mit Artnet-Plugin auf einem Raspberry laufen. Als Schnittstelle zum DMX verwende ich das Enttec Open DMX USB Interface. Wenn bei Dir sh.dmx1(1, 100) nicht funktioniert scheint noch etwas Grundsätzliches nicht zu stimmen. Kannst du denn prinzipiell mit dem AVR die DMX Kanäle ansprechen? Ich habe das bei mir mit dem Android Art-Net Controller getestet. Dort trägt man einfach die IP des Artnet Knotens ein und kann dann alle Kanäle via Smartphone manipulieren. Dieses hat mir auch in der Bauphase als mobiler Lichtschalter gedient, da ich zu dieser Zeit noch keine Verbindung zum KNX hatte.

            Hier mal ein Auszug aus meiner plugin.conf wo ich das Artnet-Plugin entsprechend eingebunden habe:

            Code:
            # /usr/local/smarthome/etc/plugin.conf
            [dmx1]
                class_name = ArtNet
                class_path = plugins.artnet
                artnet_subnet = 0
                artnet_net = 0
                artnet_universe = 1
                ip = "IP-Adresse des OLA-Servers bzw. deines AVR ArtnetNode"
                port = 6454
            Damit kann ich via sh.dmx1(...) auf alle Kanäle zugreifen. Grundsätzlich basiert mein Steuerungskonzept auf der Verwendung von Logiken. So verfügt bei mir jeder Raum über ein eigenes Logic-File mit einem Zustandsautomat welcher alle Funktionen steuert. Alle für diesen Raum relevanten Signale triggern die Raumlogik und diese führt dann in Abhängigkeit der aktuellen Raum-Betriebsart die entsprechende Aktion aus. So schaltet zum Beispiel das Signal eines Präsenzmelders nicht direkt eine Lichtquelle, sondern triggert stattdessen die Raumlogik. Dort hat man dann alle Möglichkeiten darauf zu reagieren.

            z.B. Raum im "Schlafmodus" -> Lichtquelle 1 = 30%, Raum im "Standardmodus" -> alle Lichtquellen 100%

            Zurück zum DMX. Bei der Verwendung von sh.dmx1(...) hatte ich gemerkt, dass das Setzen eines einzelnen Kanals unter Umständen zum Rücksetzen eines anderen Kanals führt. Erklärung gibt hier die "Dokumentation" des Artnet-Plugin.
            ...on a DMX bus you can not set just one specific channel. You have to begin at first channel setting your values.
            Im Prinzip werden im Artnet-Telegramm immer alle 512 Kanäle übertragen. Es ist nicht möglich nur einen Kanal anzusprechen. Um hier eine gewissen Datenkonsistenz zu erreichen, habe ich den Zugriff auf das Artnet-Plugin in einer Funktion gebündelt. Das bedeutet, es gibt es nur eine Stelle wo sh.dmx1(...) aufgerufen wird. Das ganze sieht dann so aus:

            Code:
            # File DmxUpdate.py
            def DmxUpdate(sh):
                sh.dmx1([int(sh.DG.Childroom1.Light.LED.South.Color.ColdWhite.ActualBrightness()),
                         int(sh.DG.Childroom1.Light.LED.South.Color.WarmWhite.ActualBrightness()),
                         0,
                         0,
                         int(sh.DG.Childroom1.Light.LED.Gaube.Color.Red.ActualBrightness()),
                         int(sh.DG.Childroom1.Light.LED.Gaube.Color.Blue.ActualBrightness()),
                         int(sh.DG.Childroom1.Light.LED.Gaube.Color.Green.ActualBrightness()),
                         int(sh.DG.Childroom1.Light.LED.Gaube.Color.White.ActualBrightness()),
                         0,
                         0,
                         0,
                         0,
                         int(sh.DG.Bathroom.Light.LED.West.Color.Red.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.West.Color.Blue.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.West.Color.Green.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.West.Color.White.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.South.Color.Red.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.South.Color.Blue.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.South.Color.Green.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.South.Color.White.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Mirror.Color.Red.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Mirror.Color.Blue.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Mirror.Color.Green.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Mirror.Color.White.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Shower.Color.Red.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Shower.Color.Blue.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Shower.Color.Green.ActualBrightness()),
                         int(sh.DG.Bathroom.Light.LED.Shower.Color.White.ActualBrightness()),
                         0,
                         0,
                         0,
                         0,
                         0,
                         0,
                         0,
                         0,
                         0])
                return
            Zur Erklärung: An meinem DMX hängen ausschließlich LED-Stripes via eldoLED Treiber. Für jede LED-Lichtquelle wurde in der items.conf ein eigenes Item für die Helligkeit angelegt. Diese Items werden entsprechend der Kanalzuordnung an sh.dmx1() übergeben. In diesem konkreten Fall, setzt sh.dmx1() die Werte der ersten 37 Kanäle. Um beispielsweise in einer Logik die Helligkeit einer DMX-Lichtquelle zu manipulieren sind nun folgende Anweisungen nötig:
            Code:
            sh.DG.Childroom1.Light.LED.South.Color.WarmWhite.ActualBrightness(255)
            DmxUpdate(sh)
            Nun zum Thema Dimmen. Um dies komfortabel zu ermöglichen habe ich die Item Funktion .fadeDMX eingeführt. Diese arbeitet analog zur bekannten .fade() Funktion nur via DMX.

            Code:
            # /usr/local/smarthome/lib/item.py
            
            # Standard Smarthome.py Fade-Funktion
            def fade(self, dest, step=1, delta=1):
                    dest = float(dest)
                    self._sh.trigger(self._path, _fadejob, value={'item': self, 'dest': dest, 'step': step, 'delta': delta, 'function': None})
            
            # DMX-Fade Funktion
            def fadeDMX(self, dest, step=1, delta=1):
                    # DMX erwartet als Wert einen integer
                    dest = int(dest)
                    # Die Zeit zwischen zwei Schritten auf ein Minimum begrenzen. Die Uebertragung
                    # eines DMX-Frames dauert ca. 4ms. Von daher sollte das "delta" darueber liegen
                    while delta < 0.004:
                        step += 1
                        delta *= 2
                    self._sh.trigger(self._path, _fadejob, value={'item': self, 'dest': dest, 'step': step, 'delta': delta, 'function': DmxUpdate})
            Wie zu sehen ist, besteht der einzige Unterschied, dass hier als 'function' DmxUpdate() hinterlegt ist. Nun können DMX Lichtquellen wie gewohnt direkt in der items.conf oder aus einer Logik heraus gedimmt werden. Um Beim obigen Beispiel zu bleiben:

            Code:
            sh.DG.Childroom1.Light.LED.South.Color.WarmWhite.ActualBrightness.fadeDMX(255, 1, 0.5)
            Vielleicht gibt es auch noch andere Lösungsansätze, mir ist aber nichts besseres eingefallen. Vielleicht ist es Dir ja hilfreich.

            Gruß Jörg

            Kommentar


              #7
              Hallo Jörg,

              vielen Dank für deine ausführliche und sehr hilfreiche Antwort. Sehr nett von dir.

              Ich nutze das Interface von Ulrich Radig (siehe http://shop.ulrichradig.de/Bausaetze...x-Bausatz.html, ein Weihnachtsgeschenk meiner Kinder und bin mir ehrlich gesagt nicht sicher, ob ich hier einen OLA-Server brauche. Mit dem dmxworkshop unter Windows kann ich meinen (ersten) LED-Streifen kontrollieren, mit smarthome.py und der ArtNet Controller App habe ich es noch nicht geschafft. In der App wird der ArtnetNode zwar gefunden, ich kann aber keine Werte senden, bzw. der Streifen reagiert nicht.

              Die Beispiele sind sehr hilfreich, vielen Dank nochmal. Es wäre super, wenn du noch ein Item und eine Logik-Definition posten könntest, um ein komplettes Bild zu haben.

              Es gibt übrigens einen Thread zu diesem Artnet Bausatz im Forum, siehe https://knx-user-forum.de/forum/supp...r-dmx-hardware

              Grüße
              Gerd

              Kommentar


                #8
                Hallo Gerd,

                wahrscheinlich würdest du auch mit dem Smarthome.py DMX-Plugin auskommen, dann sparst du Dir den OLA-Server. Für mich hat dieser jedoch eine Reihe von Vorteilen. Zum einen können Smarthome.py und das DMX Interface räumlich voneinander getrennt sein. Aktuell läuft Smarthome.py auf einem Raspberry im Netzwerkschrank im Keller. Der OLA-Server hingegen sitzt zusammen mit den DMX LED-Treibern in der Verteilung im 1.OG. Weiterhin kann ich über die vorhandene Netzwerkstruktur, sei es via Kabel oder WLAN, mit jedem Gerät auf den OLA-Server und damit den DMX-Bus zugreifen. Das Artnet Protokoll wird von vielen Clients und sämtlichen Plattformen unterstützt. Da ergeben sich viele zusätzliche Mögichkeiten. Ob man diese im Endeffekt auch braucht oder nutzt sei erst einmal dahingestellt.

                Items- und Logik-Definitionen werde ich noch nachreichen.

                Gruß Jörg

                Kommentar


                  #9
                  Hallo Gerd,
                  ich glaube das DMX-Plugin sendet nur über USB oder serieller Schnittstelle, das wird mit dem ArtNet vom UlrichRadig (glaub ich) nicht funktionieren..

                  Ich hab bei mir das ArtNet-Plugin im Einsatz ich kann mich nicht an die Installation eines OLA-Servers erinnern..

                  das ArtNet-Plugin sendet nicht wenn ein Item getriggert wird, sondern kann nur über eine Logik aufgerufen werden..
                  also muss das Item eine Logik triggern in welchen der Wert dann an den DMX-Kanal geschickt wird,..


                  du schreibts bei Dir funktioniert "sh.dmx1(1, 100)" nicht. wo rufst du dies denn auf? Kommt ein Fehler?

                  evtl. kannst Du im Artnet-Plugin in der Zeile 122 das "#" löschen, (vorsicht bei Python auf die Einrückungen) und sh im Debug Modus starten - um zu sehen ob das Plugin einen Wert senden,..

                  Code:
                  #       logger.info("Outgoing Artnet:%s"%(':'.join(x.encode('hex') for x in data)))
                  Gruß Ivan

                  Kommentar


                    #10
                    Hallo Ivan,

                    zum Testen rufe ich smarthomeNG.py im interaktiven Modus auf:

                    sudo systemctl stop smarthome
                    /usr/local/smarthome/bin/smarthome.py -i
                    In der interaktiven shell setze ich den Befehl
                    sh.dmx1(1,100)
                    ab... und es tut sich nichts.

                    Die von dir erwähnte Zeile habe ich aktiviert und bekomme damit die Fehlermeldung
                    ERROR Main Unhandled exception: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs
                    Wenn ich die Zeile etwas vereinfache:
                    logger.info("Outgoing Artnet:%s"%(data))
                    liefert der Befehl sh.dmx1(1,100) die Info:
                    INFO Main Outgoing Artnet:['Art-Net\x00', b'\x00P', b'\x00\x0e', b'\x01', b'\x00', b'\x00\x00', b'\x00\x02', b'd', b'\x00'] -- __init__.py:__ArtDMX_broadcast:122
                    Mit dem Dmxworkshop kann ich unter Windows die Kanäle 1 und 2 (mehr habe ich noch nicht verbunden) zum Leuchten bringen. An der Verkabelung kann es also nicht liegen.

                    Ich verwende übrigens einen Beaglebone Black mit dem Cape von ing-budde.de und seinem fertigen Image, das auf Debian/Jessie aufsetzt.

                    Hier noch zur Vollständigkeit der Auszug aus /etc/plugin.conf
                    [dmx1]
                    class_name = ArtNet
                    class_path = plugins.artnet
                    artnet_subnet = 0
                    artnet_net = 0
                    artnet_universe = 0
                    ip = 192.168.24.99
                    port = 6454
                    Danke für die Unterstützung.

                    Grüße
                    Gerd

                    Kommentar


                      #11
                      ich habe die Log Zeile im Plugin bei mir probeweise so angelegt:

                      Code:
                      logger.info('send value:{0} to ip: {1} port:{2}'.format(result, self.ip, self.port))
                      wenn ich an Kanal 1 den Wert 100 sende:

                      sh.dmx1(1,100)
                      2017-02-10 09:40:03 INFO __init__ Main send value:b'Art-Net\x00\x00P\x00\x0e\x01\x00\x00\x00\x00\x02d\x00' to ip: 192.168.0.77 port:6454 -- __init__.py:__ArtDMX_broadcast:125

                      __init__.py vom Plugin "artnet"

                      Code:
                      #!/usr/bin/env python3
                      # coding=utf-8
                      #
                      # Copyright 2013 KNX-User-Forum e.V.            http://knx-user-forum.de/
                      # Author    mode@gmx.co.uk
                      #
                      #  This file is part of SmartHome.py.    http://mknx.github.io/smarthome/
                      #
                      #  SmartHome.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.
                      #
                      #  SmartHome.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 SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
                      #
                      
                      import logging
                      import socket
                      import struct
                      
                      logger = logging.getLogger('Artnet')
                      
                      
                      class ArtNet():
                          packet_counter = 1
                          dmxdata = [0, 0]
                      
                          def __init__(self, smarthome, artnet_net, artnet_subnet, artnet_universe, ip, port):
                              self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                              self.net = int(artnet_net)
                              self.subnet = int(artnet_subnet)
                              self.universe = int(artnet_universe)
                              self.ip = ip
                              self.port = int(port)
                              logger.debug("Init ArtNet Plugin done")
                      
                          def run(self):
                              pass
                      
                          def stop(self):
                              self.close()
                      
                          def __call__(self, var1=None, var2=None):
                              if type(var1) == int and type(var2) == int:
                                  self.send_single_value(var1, var2)
                              if type(var1) == int and type(var2) == list:
                                  self.send_frame_starting_at(var1, var2)
                              if type(var1) == list and type(var2) == type(None):
                                  self.send_frame(var1)
                      
                          def send_single_value(self, adr, value):
                              if adr < 1 or adr > 512:
                                  logger.error("DMX address %s invalid" % adr)
                                  return
                      
                              while len(self.dmxdata) < adr:
                                  self.dmxdata.append(0)
                              self.dmxdata[adr - 1] = value
                              self.__ArtDMX_broadcast()
                      
                          def send_frame_starting_at(self, adr, values):
                              if adr < 1 or adr > (512 - len(values) + 1):
                                  logger.error("DMX address %s with length %s invalid" %
                                               (adr, len(values)))
                                  return
                      
                              while len(self.dmxdata) < (adr + len(values) - 1):
                                  self.dmxdata.append(0)
                              cnt = 0
                              for value in values:
                                  self.dmxdata[adr - 1 + cnt] = value
                                  cnt += 1
                              self.__ArtDMX_broadcast()
                      
                          def send_frame(self, dmxframe):
                              if len(dmxframe) < 2:
                                  logger.error("Send at least 2 channels")
                                  return
                              self.dmxdata = dmxframe
                              self.__ArtDMX_broadcast()
                      
                          def __ArtDMX_broadcast(self):
                      #       logger.info("Incomming DMX: %s"%self.dmxdata)
                              # New Array
                              data = []
                              # Fix ID 7byte + 0x00
                              data.append("Art-Net\x00")
                              # OpCode = OpOutput / OpDmx -> 0x5000, Low Byte first
                              data.append(struct.pack('<H', 0x5000))
                              # ProtVerHi and ProtVerLo -> Protocol Version 14, High Byte first
                              data.append(struct.pack('>H', 14))
                              # Order 1 to 255
                              data.append(struct.pack('B', self.packet_counter))
                              self.packet_counter += 1
                              if self.packet_counter > 255:
                                  self.packet_counter = 1
                              # Physical Input Port
                              data.append(struct.pack('B', 0))
                              # Artnet source address
                              data.append(
                                  struct.pack('<H', self.net << 8 | self.subnet << 4 | self.universe))
                              # Length of DMX Data, High Byte First
                              data.append(struct.pack('>H', len(self.dmxdata)))
                              # DMX Data
                              for d in self.dmxdata:
                                  data.append(struct.pack('B', d))
                              # convert from list to string
                              result = bytes()
                              for token in data:
                                  try:  # Handels all strings
                                      result = result + token.encode('utf-8', 'ignore')
                                  except:  # Handels all bytes
                                      result = result + token
                      #       data = "".join(data)
                              # debug
                              # send over ethernet
                              #logger.info('send value:{0} to ip: {1} port:{2}'.format(result, self.ip, self.port))
                      
                              self.s.sendto(result, (self.ip, self.port))
                      
                          def close(self):
                              self.s.close()
                      Zuletzt geändert von ivande; 10.02.2017, 09:41.

                      Kommentar


                        #12
                        ArtNet DMX Interface von Ulrich Radig sendet die Werte immer nur bis zum angegebenen Kanal.
                        Versuch mal mit
                        sh.dmx1(1,100)
                        dann
                        sh.dmx1(255,100)

                        ArtNet dazu bringen alle 255 Chanäle zu senden. Ich habe bei mir die Firmware vom ArtNet "modifiziert" dass immer alle 255Kanäle gesendet werden, da ich ein Problem mit einen 36-Kanal Receivers hatte,..

                        https://www.ulrichradig.de/forum/vie...hp?f=51&t=3215

                        Kommentar


                          #13
                          Danke für das __init__.py, bis auf die Logger Zeilen ist die Datei identisch mit meiner.

                          Hier der Output deines Log-Befehls:
                          Code:
                          2017-02-10 21:43:04 INFO     __init__     Main         Incomming DMX: [100, 0] -- __init__.py:__ArtDMX_broadcast:89
                          2017-02-10 21:43:04 INFO     __init__     Main         send value:b'Art-Net\x00\x00P\x00\x0e\x01\x00\x00\x00\x00\x02d\x00' to ip: 192.168.24.99 port:6454 -- __init__.py:__ArtDMX_broadcast:124
                          Ich hatte die subnet meines Nodes geändert. Ansonsten sind die Zeilen ja identisch.

                          Das Absetzen der beiden sh.dmx1-Befehle hilft auch nicht.

                          Merkwürdig.

                          Kommentar


                            #14
                            Versuch doch mit DMX-Workshop unter "Receive/View DMX channel grid" nachsehen ob die von SH gesendeten Werte auch im Artnet ankommen..

                            Kommentar


                              #15
                              Ich habe den ArtNet Node nochmal auf Werkseinstellungen zurück gesetzt und eine neue interne IP zugeordnet. Siehe da, ich kann ihn nun auch über smarthome.py ansprechen und die Kanäle steuern. Sonst habe ich nichts geändert.
                              Nun kann ich mich an die Programmierung machen.

                              Vielen Dank für die Unterstützung.

                              Kommentar

                              Lädt...
                              X