Ankündigung

Einklappen
Keine Ankündigung bisher.

Featurewunsch: EnOcean plugin

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

    #16
    Nur Mut - der von Max schon verlinkte Code funktioniert und benötigt lediglich die Plugin-Schnittstelle und das Parsing der ankommenden Daten, wobei da auch alles offen liegt/spezifiziert ist. Wie aber dort geschrieben muss das jemand machen der Standard-konforme Geräte hat... Wenn mir einer ein/zwei sponsert... *g*

    Bei den "Testern" muss ich immer Lächeln.
    HM-KNX - KNX-Interface für Hörmann Garagentorantriebe: http://www.ing-budde.de

    Kommentar


      #17
      Robert ich brauch nur nen nen Hardware-Tip ... dann löse ich die Bestellung aus (für MICH ).

      Sollte doch gehen oder: EnOcean USB 300 USB-Gateway 868MHz / TCM310 Chipsatz | eBay

      Jung EnOcean Funk-Wandsender 4-kanalig ENO AL 2995-L Aluminium | eBay
      Umgezogen? Ja! ... Fertig? Nein!
      Baustelle 2.0 !

      Kommentar


        #18
        Das USB-Dongle ist ok. Hauptsache TCM310 - das ist das HF-Modul was ich auch auf dem Beaglebone Cape verbaue. Schnöder UART - also stell dich auf nen normalen CDC-ACM ein.

        Schalter: Kein Plan - sieht günstig aus - hau rein.
        HM-KNX - KNX-Interface für Hörmann Garagentorantriebe: http://www.ing-budde.de

        Kommentar


          #19
          Bestellt, mal sehen ob ich es über den Jahreswechsel schaffe.
          Umgezogen? Ja! ... Fertig? Nein!
          Baustelle 2.0 !

          Kommentar


            #20
            Also die Taster liegen hier, dank des Ansatzes von Robert ist der Einstieg ja recht einfach.
            Die Doku zum Protokoll ist auch ganz o.k. -> http://www.enocean.com/esp3/

            Nun hab ich hier 4-fach Taster und frage mich was könnten die Hex/Dec-Werte bedeuten und gibts irgendwo ne ausführlichere Doku?

            Die Taster senden RPS-Telegramme und identifizieren sich als Switch mit dec=246 / hex=0xf6.

            Als "Nutzdaten kommt folgendes Dezimal beim Taster raus:

            Code:
            I--- 55 ---I
            I 48   112 I
            I          I
            I 16    80 I
            I--- 21 ---I
            o.l. = 48
            o.r = 112
            o.beide = 55
            u.l. = 16
            u.r. = 80
            u.beide = 21

            Das entspricht scheinbar einem gewissen Standard:
            Kanal1 = 0x70 (112)
            Kanal2 = 0x50 (80)
            Kanal3 = 0x30 (48)
            Kanal4 = 0x10 (16)

            Zum Beispiel hier ein Dokument von Eltako: http://www.eltako.com/fileadmin/down...k_high_res.pdf

            Viel verwirrender ist das hier:https://www.google.de/url?sa=t&rct=j...57752919,d.Yms

            Das passt wohl in kein Plugin
            Umgezogen? Ja! ... Fertig? Nein!
            Baustelle 2.0 !

            Kommentar


              #21
              http://www.enocean.com/fileadmin/red...s_EEP_V2.5.pdf - Seite 14 folgende

              Mach fettich! ;-)

              Grüße
              Robert
              HM-KNX - KNX-Interface für Hörmann Garagentorantriebe: http://www.ing-budde.de

              Kommentar


                #22
                Ja, zu viele Baustellen

                Ich frage mich nur wo man aufhört . Ich mach erstmal die Taster fertig und versuche alles was man brauchen könnte schonmal in Variablen zu packen.
                Umgezogen? Ja! ... Fertig? Nein!
                Baustelle 2.0 !

                Kommentar


                  #23
                  Also ich hab den einfachen Taster schonmal drin.

                  Wie stellt Ihr euch eigentlich die Konfiguration der Items vor? Der 4-Fach-Taster ist ja schon recht komplex mit 6 Varianten: A1,B1,A0,B0,A1+B1,A0+B0

                  Wie sollen denn da die items aussehen?

                  Code:
                  [eno]
                      [A0]
                      type = bool
                      enforce_update = true
                      eno_id = 00:11:22:33:44
                      eno_rorg = F6_02
                      eno_button = A0
                      [A1]
                      type = bool
                      enforce_update = true
                      eno_id = 00:11:22:33:44
                      eno_rorg = F6_02
                      eno_button = A1
                      [B0]
                      type = bool
                      enforce_update = true
                      eno_id = 00:11:22:33:44
                      eno_rorg = F6_02
                      eno_button = B1
                      [B0]
                      type = bool
                      enforce_update = true
                      eno_id = 00:11:22:33:44
                      eno_rorg = F6_02
                      eno_button = B0
                  Später bräuchte es da eine recht große Menge an Telegrammdefinitionen.

                  Einen Vorteil hat das ganze ... ich hab jetzt endlich mal die Bit-Operatoren verstanden ... komisches Gemüse.


                  EDIT:
                  Wer hat denn Hardware zu liegen (Taster UND Interface)?
                  Umgezogen? Ja! ... Fertig? Nein!
                  Baustelle 2.0 !

                  Kommentar


                    #24
                    Hi! Freut mich dass es klappt!

                    id und rorg sehen gut aus.

                    Warum "eno" - "enocean" ist doch nun auch nicht soo lang. Die Leute schreiben ja auch "squeezebox_send" und murren nicht *g*.

                    Ein Blick auf die anderen EEP müsste die Reise vorbestimmen: Implementiert man ins Plugin verschiedene "rorg-Klassen"? Ein Dict mit den "rorg" wie bei dir angegeben und als value dann unterstützte "button" mit je einer Transformationsfunktion?

                    Dürfte so auch gut zu prüfen sein.

                    Aus diesem Grund würde ich nicht "enocean_button" sagen sondern "enocean_entity" und dann können da eben alle Wertefelder eingetragen werden, die zuvor in dem von "RORG" abhängigen dict gefunden wurden. Ich meine Ähnlich ist es auch im Onewire-Plugin, wo das "Entity-Set" von der Onewire-ID (und der unsäglichen Page.3) abhängig ist.

                    Wie gesagt, ich würde dann einen Transformator für jede Entität schreiben, der auf den Payload losgelassen wird. Dann kann man neue EEPs durch erweitern des Dictionaries bequem hinzufügen.

                    Grüße
                    Robert

                    Ach so, kurz erläutert:

                    x = {'F6_02':[('B0',<nimm Bit 2 als Funktion aber nur wenn nicht x>), ('A1',<nimm Bit 3 wenn...>)], 'F6_xx ...........}

                    Dann beim Parsen:

                    wenn "encoean_rorg" nicht in x -> Möööp
                    wenn "enocean_entity" nicht in x(enocean_rorg) -> Mööp


                    Bei Telegramm dann über "enocean_id" Item bestimmen und über x(rorg) alle Transformationen auf den Payload laufen lassen.

                    Vielleicht kann Marcus ja mit den Transformationen helfen. Würde das ähnlich den "eval" der Items machen!
                    HM-KNX - KNX-Interface für Hörmann Garagentorantriebe: http://www.ing-budde.de

                    Kommentar


                      #25
                      Also das was ich ad hoc verstanden habe (75%) klingt sinnig. Davon irgendwelche Namen zu vergeben bin ich noch ein bisschen entfernt. aber meinetwegen auch enocean (ist aber son Fingerkiller).

                      RORG kommt ja aus dem Telegramm, FUNC und TYPE müsste man wissen, ist aus den "Unterlagen" zu meinen Jung Tastern aber auch nicht ersichtlich.

                      Mit meinen Worten:

                      Es gibt ein dict in dem steht die jeweilige Methode mit der payload bearbeitet wird. Ob wir das Telegramm überhaupt bearbeiten und mit welcher Methode entscheidet die SenderID zusammen mit der item.conf.

                      Gibts ein "Funktionsupdate" de Plugins kommt ein neue Methode und ein Eintrag im dict hinzu.

                      Weils nicht ganz offtopic ist:
                      Man könnte eine Übersicht gebrauchen über welche Methoden ein Plugin in sh.py verfügen sollte und eine timeline wie diese abgearbeitet werden ... brauch ich aber konkret für ein anderes Plugin.
                      Umgezogen? Ja! ... Fertig? Nein!
                      Baustelle 2.0 !

                      Kommentar


                        #26
                        Eine Frage taucht auf ... was ist effektiver/schneller/besser:

                        Code:
                        x=21
                        a = bitstring.BitArray(uint=x,length=8)
                        print(a[:3])
                        Code:
                        print((0x15 & 0xE0) >> 5)
                        Wobei ich das "a" von bitstring natürlich für weitere Operationen wieder verwenden kann. Die zweite Variante braucht dafür kein bitstring-Modul. Ich kann ja seit heute Bit-Schubsen .
                        Umgezogen? Ja! ... Fertig? Nein!
                        Baustelle 2.0 !

                        Kommentar


                          #27
                          @Robert:
                          Was hast Du denn an Sensoren für EnOcean rum zu liegen? Auch wenn das selbst gefertigte Dinge sind gehe ich doch davon aus dass Du Dich an das EnOcean Protokoll gehalten hast?

                          @all:
                          Ich sehe hier ne ziemlich komplexe Geschichte was die Config angeht.
                          Eigentlich gibt es mind. 3 große Filter.

                          1. Header auswerten und sehen um was für einen PacketType es sich handelt -> Packet Type.
                          An dieser Stelle kann ich noch nicht überblicken was man alles implementieren möchte/muss. RADIO scheint hier erstmal Standard(das wichtigste) zu sein.
                          Code:
                          0x00  ---  Reserved
                          0x01  RADIO  Radio telegram
                          0x02  RESPONSE  Response to any packet
                          0x03  RADIO_SUB_TEL  Radio subtelegram
                          0x04  EVENT  Event message
                          0x05  COMMON_COMMAND  Common command
                          0x06  SMART_ACK_COMMAND  Smart Ack command
                          0x07  REMOTE_MAN_COMMAND  Remote management command
                          0x08  ---  Reserved for EnOcean
                          0x09  RADIO_MESSAGE  Radio message
                          0x0A  RADIO_ADVANCED  Advanced protocol radio telegram
                          2. Abhängig vom Header/PacketType muss dann jeweils! der TelegramType ausgewertet werden -> Telegram Type
                          Code:
                          0XF6 RPS
                          0XD5 1BS
                          0xA5 4BS
                          0XD2 VLD
                          u.s.w.
                          3. Wiederum abhängig davon gibt es in jedem TelegramType teilweise unzählige Sub-Typen. Diese werden aber nicht! im Telegramm gesendet, die muss man dann wissen. Ausserdem hat jeder Telegramm-Type wieder unterschiedliche Objekte und Wertebereiche.
                          Seite 2: http://www.enocean.com/fileadmin/red...s_EEP_V2.5.pdf

                          Ich bekomme da nen Knoten im Kopf wie man das vernünftig strukturieren soll. Ich habe auch keine Lust anschließend 20 Seiten Doku zum Plugin zu schreiben um das verständlich zu machen. Gefühlt muss eigentlich auch nach 3 spezifischen Einträgen in der item.conf Schluss sein.

                          Ich hänge einfach mal den aktuellen Plugin-Stand an. Der macht aber nix weiter als print-Ausgaben im Debug Modus. Nicht alles was in der item.conf steht wird derzeit auch "verarbeitet".

                          Eine erste Idee wäre mit getattr und einer zusätzlichen class zu arbeiten.

                          items/item.conf
                          Code:
                          [A1]
                          type = bool
                          enforce_updates = true
                          enocean_id = 00:22:60:37
                          enocean_rorg = F6_02_02
                          enocean_rorg_id = A1 #Button-Name in Plugin
                          
                          [A0]
                          type = bool
                          enforce_updates = true
                          enocean_id = 00:22:60:37
                          enocean_rorg = F6_02_02
                          enocean_rorg_id = A0 #Button-Name in Plugin
                          plugin/enocean/__init__.py
                          Code:
                          #!/usr/bin/python
                          
                          import serial
                          import os
                          import sys
                          import logging
                          import struct
                          import time
                          
                          #CO_RD_VERSION 3
                          
                          FCSTAB = [
                              0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
                              0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
                              0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
                              0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
                              0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
                              0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
                              0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
                              0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
                              0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
                              0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
                              0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
                              0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
                              0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
                              0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
                              0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
                              0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
                              0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
                              0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
                              0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
                              0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
                              0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
                              0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
                              0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
                              0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
                              0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
                              0x76, 0x71, 0x78, 0x7f, 0x6A, 0x6d, 0x64, 0x63,
                              0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
                              0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
                              0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
                              0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8D, 0x84, 0x83,
                              0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
                              0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3
                              ]
                          
                          DEVICE = dict(
                              switch = 246,  # 0xf6 RPS, org 05
                              contact = 213, # 0xd5 1BS, org 06
                              sensor = 165,  # 0xa5 4BS, org 07
                              msc = 209,     # 0xd1 MSC
                              vld = 210,     # 0xd2 VLD
                              ute = 212     # 0xd4 UTE
                          )
                          
                          VALID_ID = dict()
                          
                          logger = logging.getLogger('EnOcean')
                          
                          class EnOceanType():
                              def switch():
                                  pass
                          
                          class EnOcean():
                              
                              _items = []
                              
                              def __init__(self, smarthome, serialport):
                                  self._sh = smarthome
                                  self.port = serialport
                                  self._tcm = serial.Serial(serialport, 57600, timeout=0.5)
                          
                              def run(self):
                                  self.alive = True
                                  global from_shell
                                  from_shell = False
                                  self.connect()
                                  msg = []
                                  while True:
                                      readin = self._tcm.read(1000)
                                      if readin:
                                          msg += readin     
                                          # check if header is complete (6bytes including sync)
                                          # 0x55 (SYNC) + 4bytes (HEADER) + 1byte(CRC)
                                          while (len(msg) >= 5):
                                              #check header for CRC
                                              if (msg[0] == 0x55) and (self._calc_crc8(msg[1:5]) == msg[5]):
                                                  # header bytes: length of data; length of data; optional length; packet type
                                                  ### HEADER RECEIVED ###
                                                  print ('header: {}'.format(msg[:6]))
                                                  ### HEADER_DATA_LENTGH ###
                                                  data_length = (msg[1] << 8) + msg[2] #data
                                                  print ("data_length:{}".format(data_length))
                                                  ### HEADER_OPT_DATA_LENGTH ###
                                                  opt_length = msg[3]                  #opt
                                                  print ("opt_length:{}".format(opt_length))
                                                  ### HEADER_PACKET_TYPE ###
                                                  packet_type = msg[4]
                                                  print ("packet_type:{}".format(packet_type))
                                                  ### CALCULATED MESSAGE_LENTGH FROM HEADER ###
                                                  msg_length = data_length + opt_length + 7
                                                  print ("msg_length:{}".format(msg_length))
                                                  # check if data is complete (and ok):
                                                  if (len(msg) < msg_length):
                                                      break
                                                  if (self._calc_crc8(msg[6:msg_length-1]) == msg[msg_length-1]):
                                                      self._complete =  True
                                                      ### RECEIVED ###
                                                      print ('RX: {}'.format(msg[0:msg_length])) #print data
                                                  
                                                  # check complete check if packet_type = RADIO (0x01):
                                                  if (self._complete) and (packet_type == 0x01):
                                                      ### DATA ###
                                                      data=msg[6:msg_length-(opt_length+1)]
                                                      print ("data:{}".format(data))
                                                      ### RORG ###
                                                      rorg = data[0]
                                                      print ("device:{}".format(rorg))
                                                      ### PAYLOAD ###
                                                      payload = data[1:-5] #offset 5 bytes to ignore at end - ignore rorg too
                                                      print ("payload:{}".format(payload))
                                                      ### SENDER_ID ###
                                                      #print (self.build_mac(data[2:6]))
                                                      sender_id = self.build_mac(data[2:6])
                                                      print ("SenderID: {}".format(sender_id))
                                                      
                                                      ### OPT_DATA ###
                                                      opt=msg[(6+data_length):msg_length-1]
                                                      if len(opt) > 0:
                                                          print ("opt:{}".format(opt))
                                                          ### SUB_TEL_NUMBER ###
                                                          print ("SubTelNumber: {}".format(opt[0]))
                                                          ### DESTINATION ###
                                                          print ("Destination: {}".format(self.build_mac(opt[1:5])))
                                                          ### LINK_QUALITY ###
                                                          print ("dBm: -{}".format(opt[5]))
                                                          ### SECURITY_LEVEL ###
                                                          print ("SecurityLevel: {}".format(opt[6]))                            
                                                          ### CRC ###
                                                          print ("crc:{}".format(msg[msg_length-1]))
                          
                                                      print ("VALIDATE")
                                                      self.validate(rorg, sender_id, payload, opt)
                          
                                                  msg = msg[msg_length:]
                                              else:
                                                  msg.pop(0)
                                              
                                                  
                          #            self.radio([0xA5, 0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x00, 0x30])   # 4BS + 9bytes Data
                          #            self.radio([0xF6, 0x66, 0x00, 0x00, 0x00, 0x00, 0x20])                 # RPS
                          
                              def validate(self, rorg, sender_id, payload, opt):
                                  #think about this example:
                                  #x = {'F6_02':[('B0',<nimm Bit 2 als Funktion aber nur wenn nicht x>), ('A1',<nimm Bit 3 wenn...>)], 'F6_xx ...........}
                                  valid = False
                                  for item in self._items:
                                      # validate id for item id:
                                      if item.conf['enocean_id'] == sender_id:
                                          print ("validated {0} for {1}".format(sender_id,item))
                                          valid = True
                                          if valid:
                                              #### 0xF6 SPECIFIC RPS ####
                                              if rorg == 0xF6: #0xf6
                                                  print ("F6-02-02 Command-Set RPS")
                                                  spec_payload = payload[0]
                                                  print ("payload: {}".format(spec_payload))
                                                      #a = bitstring.BitArray(uint=payload,length=8)
                                                  x = spec_payload
                                                      #print(a[:3])         # last 3 bits
                                                  button_1 = (ord(chr(x)) & 0xE0) >> 5
                                                  print ("First Button-ID: {}".format((ord(chr(x)) & 0xE0) >> 5)) #sixth to eigth
                                                      #print(a[3:4])        # 4th bit
                                                  pressed = (ord(chr(x)) & 0x10) >> 4
                                                  print ("Pressed: {}".format((ord(chr(x)) & 0x10) >> 4)) #fifth
                                                      #print(a[-4:-1])      # second to fourth bit
                                                  button_2 = (ord(chr(x)) & 0x0E) >> 1
                                                  print ("Second Button-ID: {}".format((ord(chr(x)) & 0x0E) >> 1)) #second to fourth
                                                      #print(a[:-1])         # first bit
                                                  valid_2 = (ord(chr(x)) & 0x01)
                                                  print ("Second Button is valid: {}".format((ord(chr(x)) & 0x01))) #first
                                                  button_codes=['A1','A0','B1','B0']                    
                                                  if pressed == 1:
                                                      if valid_2 == 1:
                                                          button_result = button_codes[button_1]+button_codes[button_2]
                                                      else:
                                                          button_result = button_codes[button_1]
                                                      print ("Resulting Button: {}".format(button_result))                    
                                                  #### END OF 0xF6 SPECIFIC #### 
                                                  
                                              #### 0xD5 SPECIFIC 1BS ####
                                              if rorg == 0xD5: #0xD5
                                                  print ("D5 Command-Set 1BS")
                                                  
                                              #### 0xA5 SPECIFIC 4BS ####
                                              if rorg == 0xA5: #0x00
                                                  print ("A5 Command-Set 4BS")
                                                  
                                              #### 0x00 SPECIFIC Extended Telegram ####
                                              if rorg == 0x00: #0x00
                                                  print ("00 Identification-Set")
                                                  
                              def parse_item(self, item):
                                  if 'enocean_id' in item.conf:
                                      enocean_id = item.conf['enocean_id']
                                      
                                      if 'enocean_rorg' in item.conf:
                                          enocean_rorg = item.conf['enocean_rorg']
                                      
                                          self._items.append(item)
                                          #return self.update_item 
                                      
                              def update_item(self, item, caller=None, source=None, dest=None):
                                  if caller != 'EnOcean':
                                      if item():
                                          pass
                                          #if 'roomba_cmd' in item.conf:
                                          #    cmd_string = item.conf['roomba_cmd']
                                          #    logger.debug("Roomba: item = true")
                                          #    self.drive(cmd_string)
                          
                              def build_mac(self,ints):
                                  hex_string = ''
                                  for i in ints:
                                      hex_string += "%0.2X" % i
                                  hex_string = ':'.join(hex_string[i:i+2] for i in range(0,8,2))
                                  return hex_string
                          
                              def stop(self):
                                  self.alive = False
                          
                              def connect(self):
                                  logger.debug("EnOcean: connect")
                                  # request version information / firmware
                                  self._send([0x55, 0x00, 0x01, 0x00, 0x05, 0x00, 0x03, 0x00])
                                  #0x03 = CO_RD_VERSION = Read the device (SW) version / (HW) version, chip ID etc.
                                  
                              def radio(self, msg):
                                  self._send([0x55, (len(msg) >> 8), (len(msg) & 0xFF), 0x07, 0x01, 0x00] + msg + [0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00])
                                  # SYNC,DataLength,DataLength,OptLength,PacketType,CRC + DATA + SubTelNum,DestinationID,DestinationID,DestinationID,DestinationID,dBm,SecurityLevel
                                  # 0xFF 0xFF 0xFF 0xFF ---> Broadcast
                          
                              def _calc_crc8(self, msg, crc=0):
                                  for i in msg:
                                      crc = FCSTAB[crc ^ i]
                                  return crc
                                  
                              def _send(self, msg):
                                  msg[5] = self._calc_crc8(msg[1:5])
                                  msg[-1] = self._calc_crc8(msg[6:-1])
                                  send = msg
                                  print ('TX: {}'.format(msg))
                                  self._tcm.write(bytearray(msg))
                          
                          def main():
                              try:
                                  if sys.argv[1]:
                                      serialport = sys.argv[1]
                                      print ("TCM310 on {0}".format(serialport))
                                  smarthome = ''
                                  global from_shell
                                  from_shell = True
                                  tcm = EnOcean(smarthome,serialport)
                                  tcm.run()
                          
                              except Exception as e:
                                  print ("[main]: {0}".format(e))
                                  print ("usage: __init__.py <serial_port>")
                                  return 1
                          
                          if __name__ == "__main__":
                              sys.exit(main())
                          etc/plugin.conf
                          Code:
                          [enocean]
                              class_name = EnOcean
                              class_path = plugins.enocean
                          	serialport = /dev/ttyUSB0

                          Also Achtung ... diese Plugin kann noch nichts

                          Macht es Sinn das ganze zur Diskussion schonmal nach develop zu schieben -> Lesbarkeit im Browser ?

                          Grüße
                          Umgezogen? Ja! ... Fertig? Nein!
                          Baustelle 2.0 !

                          Kommentar


                            #28
                            Zuerst mal: Daumen hoch für deine Arbeit!

                            Klar, denke das sollte unbedingt jetzt schon ins develop. Je nach dem, wie produktiv du das nutzt und es magst, kann ich direkt versuchen mitzuhelfen oder zumindest testen.

                            Ich habe ein nur Evaluation Board mit integr. Programmer (EPK 350 - ENOCEAN - PROGRAMMER KIT, ESK300 UPGRADE | Farnell Deutschland) und eine handvoll zusätzliche TCM300. Ziel ist es, nur mit dem CC3000 (8051 Architektur) eine Ultraschallmessung zu realisieren (wie SR-04, aber ohne extra Controller - der SR-04 hat ja schon einen und dann noch einen dazwischen - neee) und das ganze per Solarenergie zu speisen. Braucht aber noch ein wenig. Ansonsten kann ich aber das ESK als 4-Button-Rocker parametrieren (bzw. wenn ich wollte auch alles andere, von x-fach Eingang bis x-fach Ausgang. Nur die Zeit.... Da ist ein "echter" Schalter angenehmer.

                            Nun zur Diskussion.

                            Gedanke vorweg: Mir schwante auch schon, dass sich die mannigfaltigen EEP Datenformate nur schwer abbilden lassen werden. Hier ist die Frage, ob man den Benutzer es möglicht einfach machen will, und er nur ein Minimum angeben muss (im Zweifel sogar ein konkretes Produkt), oder von ihm verlangt zumindest die korrekte RORG etc. zu kennen. Ersteres macht das Plugin möglicherweise unnötig unflexibel, letzteres verlangt vom Benutzer mehr Einsatz. Ich würde letzteres bevorzugen. So kapselt das squeezebox-Plugin ja auch nicht die Befehle sondern verlangt vom Benutzer die Befehle zu kennen. Nur benötigte Intelligenz welche durch das Protokoll nicht einfach möglich sind (start/stop/pause oder die Mute-Sachen) erfahren eine Extrabehandlung. Aber so konnten sich andere Nutzer einfach Funktionen zurechtdengeln, die ich gar nicht auf dem Schirm hatte.

                            Konkreter: Genau wie du es aufgeschlüsselt hast sehe ich es auch. Der Telegrammtyp muss halt verarbeitet werden. Sicher werden die für die unmittelbare Funktion interessanten Telegramme immer den gleichen Typ haben. Der RORG und die Adresse werden dann überprüft und falls nicht passend ein Fehler ausgegeben. Da ist über den Vergleich die erste Sicherheitsebene vorhanden.

                            Also:
                            1. Adresse prüfen ob in einem Dict unserer "Items" (also einfach {Addr:Item-Ref, Addr2, Item2-Ref})
                            2. Telegrammtyp ermitteln und gucken ob Datenpaket (andere später!)
                            3. Über Item-Ref von oben prüfen, ob erster Teil des "enocean_rorg" Attribut mit der RORG im Paket übereinstimmt.
                            4. Basierend auf der "enocean_rorg" in einem ZWEITEN Dict wie oben beschrieben a) die zulässigen "rorg_id" (heißt das wirklich so?) mit dem Attribut vergleichen. b) Dann mit der hinterlegten Transformationsfunktion (aus dem DICT!) konvertieren.


                            Vorteil:
                            Ein Dict was durch "parse_items" gefüllt wird und nur Referenzen auf die Items beinhaltet. Dessen "enocean..."-Attribute werden dann bei Bedarf gelesen. Machen ja einige Plugins so.
                            Ein Dict, wo wenn jemand ein "neues" Gerät hat die entsprechenden RORG, "RORG-ID" und Transformationsfunktionen nachgeplfegt werden können, ohne neue Abfragen/Handler im eigentlichen Code zu schreiben.

                            Ein wenig habe ich das im SMA-Plugin realisiert, wo aber keine Transformationsfunktionen benutzt werden müssen sondern nur Speicheradresse abhängig vom Feld gelesen werden. Dadurch kann man diese Lesezugriffe auch noch Zusammenfassen.

                            Auf diese Weise kommst du auch mit 3 Attributen aus: MAC, RORG, ID (wobei ich das wirklich als "select", "entity" oder "func" bezeichnen würde um Verwechselungen zu vermeiden). Diese können dann ja wie oben beschreiben sequentiell abgeprüft werden (MAC-Match (in dict1), RORG-Match (in Item-Attr), ID-Match (in dict2).

                            Grüße
                            Robert
                            HM-KNX - KNX-Interface für Hörmann Garagentorantriebe: http://www.ing-budde.de

                            Kommentar


                              #29
                              Ich versuche mal ein paar Dinge klarer zu machen.

                              Zitat von Robert Beitrag anzeigen
                              Klar, denke das sollte unbedingt jetzt schon ins develop. Je nach dem, wie produktiv du das nutzt und es magst, kann ich direkt versuchen mitzuhelfen oder zumindest testen.
                              Nein produktiv ist das nicht, es kann ja noch nix wirklich...da sind einfach noch zu viele Fragezeichen bzgl. der Struktur.

                              Zitat von Robert Beitrag anzeigen
                              Hier ist die Frage, ob man den Benutzer es möglicht einfach machen will, und er nur ein Minimum angeben muss (im Zweifel sogar ein konkretes Produkt), oder von ihm verlangt zumindest die korrekte RORG etc. zu kennen. Ersteres macht das Plugin möglicherweise unnötig unflexibel, letzteres verlangt vom Benutzer mehr Einsatz. Ich würde letzteres bevorzugen. So kapselt das squeezebox-Plugin ja auch nicht die Befehle sondern verlangt vom Benutzer die Befehle zu kennen. Nur benötigte Intelligenz welche durch das Protokoll nicht einfach möglich sind (start/stop/pause oder die Mute-Sachen) erfahren eine Extrabehandlung. Aber so konnten sich andere Nutzer einfach Funktionen zurechtdengeln, die ich gar nicht auf dem Schirm hatte.
                              Ich denke man sollte vom Nutzer schon wenigstens die Konkrete RORG-ID verlangen können. Ohne jetzt tausend Datenblätter zu durchwühlen ... einen Tod muss man sterben. Rocker sind ja recht easy und kann man im Zweifel ausprobieren, Sensoren haben Datenblätter. Ansonsten muss der Support des Herstellers ran. Gute Übersicht für den "Standard" bietet Batterielose Funksensormodule, Funkempfangs- und Transceivermodule sowie Zubehör in 868 MH | EnOcean - Produkte.

                              Ich bin ehrlich: Ich habe keine Lust hier hunderte Stunden zu versenken.

                              Zitat von Robert Beitrag anzeigen
                              Konkreter: Genau wie du es aufgeschlüsselt hast sehe ich es auch. Der Telegrammtyp muss halt verarbeitet werden. Sicher werden die für die unmittelbare Funktion interessanten Telegramme immer den gleichen Typ haben. Der RORG und die Adresse werden dann überprüft und falls nicht passend ein Fehler ausgegeben. Da ist über den Vergleich die erste Sicherheitsebene vorhanden.
                              Ja das ist ja soweit sinnig und einfach.

                              Zitat von Robert Beitrag anzeigen
                              Adresse prüfen ob in einem Dict unserer "Items" (also einfach {Addr:Item-Ref, Addr2, Item2-Ref})
                              Telegrammtyp ermitteln und gucken ob Datenpaket (andere später!)
                              Über Item-Ref von oben prüfen, ob erster Teil des "enocean_rorg" Attribut mit der RORG im Paket übereinstimmt.
                              Das ist in dem ersten Beispiel ja schon teilweise so - RORG hatte ich icht eingebaut da die Gesamtstruktur noch zu unklar ist. Jetzt kommt der Punkt den ich nicht ganz verstehe:
                              Zitat von Robert Beitrag anzeigen
                              Basierend auf der "enocean_rorg" in einem ZWEITEN Dict wie oben beschrieben a) die zulässigen "rorg_id" (heißt das wirklich so?) mit dem Attribut vergleichen. b) Dann mit der hinterlegten Transformationsfunktion (aus dem DICT!) konvertieren.

                              Vorteil:
                              Ein Dict was durch "parse_items" gefüllt wird und nur Referenzen auf die Items beinhaltet. Dessen "enocean..."-Attribute werden dann bei Bedarf gelesen. Machen ja einige Plugins so.
                              Ein Dict, wo wenn jemand ein "neues" Gerät hat die entsprechenden RORG, "RORG-ID" und Transformationsfunktionen nachgeplfegt werden können, ohne neue Abfragen/Handler im eigentlichen Code zu schreiben.
                              Wie soll das realisiert werden? Bleiben wir beim Schalter, wie die Info zum gedrückten Schalter sich zusammenbaut steht ja im Code. Da braucht es aber neue Hilfsvariablen/Items ... nenn es wie Du willst. Um eindeutig zu identifizieren welcher Taster gedrückt wurde könnte man das:

                              a) über feste Werte machen (16,48,80,112,21,55)
                              b) über die entsprechenden Bits im Datenbyte + Plugin-Logik machen (So ist jetzt mal umgesetzt)

                              a) ist einfach eingebaut, aber nicht auf alles zu übertragen

                              Was ich immer noch nicht verstanden habe ist wie der Transformator aussehen soll? Das Transformationsdict liegt dann im Plugin?
                              Kannst Du das kurz mal zu diesem Beispiel angeben wenn wir davon ausgeben dass wir die items A1,A0,B1,B0,A1B1,A0B0 haben? Es geht nicht um den konkreten Code eg sondern eher was da konkret in welchem dict steht und wie die item.conf aussehen soll.

                              Code:
                              print ("F6-02-02 Command-Set RPS")
                              spec_payload = payload[0]
                              print ("payload: {}".format(spec_payload))
                              x = spec_payload
                              button_1 = (ord(chr(x)) & 0xE0) >> 5
                              print ("First Button-ID: {}".format((ord(chr(x)) & 0xE0) >> 5)) #sixth to eigth
                              pressed = (ord(chr(x)) & 0x10) >> 4
                              print ("Pressed: {}".format((ord(chr(x)) & 0x10) >> 4)) #fifth
                              button_2 = (ord(chr(x)) & 0x0E) >> 1
                              print ("Second Button-ID: {}".format((ord(chr(x)) & 0x0E) >> 1)) #second to fourth
                              valid_2 = (ord(chr(x)) & 0x01)
                              print ("Second Button is valid: {}".format((ord(chr(x)) & 0x01))) #first
                              button_codes=['A1','A0','B1','B0']                    
                              if pressed == 1:
                                  if valid_2 == 1:
                                      button_result = button_codes[button_1]+button_codes[button_2]
                                  else:
                                      button_result = button_codes[button_1]
                                  print ("Resulting Button: {}".format(button_result)) #possible reults: A1,A0,B1,B0,A1B1,A0B0
                              Umgezogen? Ja! ... Fertig? Nein!
                              Baustelle 2.0 !

                              Kommentar


                                #30
                                Zitat von JuMi2006 Beitrag anzeigen
                                Was ich immer noch nicht verstanden habe ist wie der Transformator aussehen soll? Das Transformationsdict liegt dann im Plugin?
                                Kannst Du das kurz mal zu diesem Beispiel angeben wenn wir davon ausgeben dass wir die items A1,A0,B1,B0,A1B1,A0B0 haben?
                                PHP-Code:
                                my_items = {'mac_as_str_or_bytes':[item-ref],
                                            
                                '2nd mac':[item2-refitem3-ref]}
                                my_dict = {'F6_02_02':{'A1':'pl & (1<<4) == (1<<4)','A0':'pl & (1<<5) == (1<<5)'}, 
                                           
                                'F6_02_03':{'foo':'foo_transform'}}
                                # in my_dict können natürlich auch komplexere Operation zum finden des Wertes gemacht werden! auch ein 'foo if bla else None' ginge...

                                # example frame received - yes, mac is too short i know
                                frame bytes([0xDE0xAD0xBE0xEF0xF6, (1<<5)])

                                #iterate over all items having this MAC
                                if not frame[1:4in my_items:
                                    print(
                                'unknown MAC')
                                    return

                                for 
                                item in my_items[frame[1:4]]:

                                    
                                # match rorg with item:
                                    
                                if not item.conf['enocean_rorg'][3] == frame[5]:
                                       print(
                                'wrong RORG')
                                       continue

                                    if (
                                item.conf['enocean_rorg'in my_dict) and (item.conf['enocean_id'in my_dict[item.conf['enocean_rorg']]):
                                        
                                pl frame[4:]
                                        
                                # for id = 'A0' this will be 'True' but for 'A1' this will be evaluated as 'False'
                                        
                                value = eval(my_dict[item.conf['enocean_rorg']][item.conf['enocean_id']])
                                        
                                # allow "None" to enable "eval" to decide not to update item!
                                        
                                if value is not None:
                                              
                                item(value,'cooles Plugin'
                                Ganz bewusst habe ich die Attribute immer wieder neu referenziert - da würde man sonst sich was kürzeres zuweisen...

                                Hinweis: Dass die 'enocean_rorg' in my_dict und "enocean_id" in my_dict['enocean_rorg'] vorhanden ist, könnte bereits einmalig zu Beginn bei 'parse_items' sichergestellt werden. Item, für deren rorg/id-Attribut keine Transform-Vorschrift existiert würde so gar nicht erst aufgenommen und somit würden derlei Pakete auch keine MAC matchen -> Problem gelöst.

                                Aso: und man könnte noch ein direktes "enocean_eval"-Attribut einführen: Dann wird nur "enocean_rorg" als 1-Byte-Wert angegeben und man schreibt die Transformationsfunktion als Attribut. Würde dann ermöglichen neue Funktionen erst mal ohne Modifikation des Plugins zu testen.
                                HM-KNX - KNX-Interface für Hörmann Garagentorantriebe: http://www.ing-budde.de

                                Kommentar

                                Lädt...
                                X