Ankündigung

Einklappen
Keine Ankündigung bisher.

Support Thread für das Russound Plugin

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

    Support Thread für das Russound Plugin

    Der Titel sagt es: Support für das Russound Plugin gibt es hier ...


    #2
    Das Russound Plugin hat im aktuellen develop zwei neue Funktionen bekommen suspend() und activate(). Die Funktionen steuern die Aktivität des Plugins. Sie können z.B. über eine Logik aufgerufen werden die von einem Item für eine schaltbare Steckdose getriggert wird.

    Siehe diesen Thread hier logik-warten-lassen-bis-russound-an und diesen Thread SHNG stürzt ab

    Kommentar


      #3
      Moin,

      aufgrund von geplanten Änderungen am SmartPlugin überlegen wir, die suspend-Funktionalität anzupassen. Die Änderungen würden sich insoweit auswirken, dass das Plugin nicht "stummgeschaltet" wird (wie jetzt in suspend), sondern angehalten (über stop()) und entweder von Hand oder per Item mit run() wieder gestartet wird.

      Das setzt voraus, dass das Plugin "restartable" ist, also bei stop() alles Notwendige aufräumt und bei run() alles Notwendige wieder startet, ohne dass init(), parse_item() o.ä. notwendig wäre.
      Das heißt insbesondere, dass bei zusätzlichen Threads diese in run() neu erstellt werden müssen (und nicht gestartet), da Threads aufgrund eines Fehlers (?) in Python nicht neugestartet werden können.

      Dazu wäre die Frage: gibt es Funktionen, die das Plugin bei suspend() ausführen (können) soll? Oder würde stop/run für euch den gleichen Zweck erfüllen?

      Wenn wir das umsetzen, kann ich bei den ggf. notwendigen Änderungen gern unterstützen.

      Gruß
      Sebastian

      Kommentar


        #4
        Ich denke der Einzige der dieses Plugin zur Zeit nutzt ist Art Mooney

        Kommentar


          #5
          Hallo zusammen, danke für die Info. Ich nutze die suspend-Funktionalität derzeit nicht.

          Bin ich wirklich der einzige, der das Plugin nutzt? Die Russounds werden wohl langsam alt.
          Cheers
          Art Mooney

          Kommentar


            #6
            Morg Art Mooney bmx Nein, ich nutze auch das Russound Plugin (nun seit ca 2-3 Jahren problemlos) und bin sehr zufrieden damit. Danke für alle die daran gearbeitet haben. Jedoch nutze ich auch die suspend Funktion (noch) nicht.

            Bin gerade daran mein smarthomeNG setup auf das alternative docker zu transferieren. Der Docker verwendet die neuere Russound Version 1.7.1 - Mein altes setting war noch 1.6.0. Es funktioniert soweit auch alles zumindest soweit ich das überprüfen konnte. Nur erhalte ich jetzt im docker dies im log
            Code:
            2024-11-16  19:46:46 ERROR    lib.network         lib.network (TCP_Client_192.168.33.xxx:9621) receive in terminator mode calling data_received_callback <bound method Russound.found_terminator of <plugins.russound.Russound object at 0x7f4d0c6a43d0>> failed: found_terminator() takes 2 positional arguments but 3 were given -- If stack trace is necessary, enable/check debug log​

            Muss man mit dem upgrade noch irgendwas berücksichtigen? Habe meine items und plugin settings 1 zu 1 übernommen

            hier ein Beispiel eines items

            Code:
                        audio:
                            status:
                                type: bool
                                rus_path: 1.4.status
                                knx_dpt: 1
                                knx_send: 6/0/0
                                knx_listen: 6/0/0
            
                                volume:
                                    type: num
                                    rus_path: 1.4.volume
                                    knx_dpt: 5
                                    knx_send: 6/0/1
                                    knx_listen: 6/0/1
            
                                bass:
                                    type: num
                                    rus_path: 1.4.bass
                                    knx_dpt: 6
                                    knx_send: 6/0/2
                                    knx_listen: 6/0/2
            
                                treble:
                                    type: num
                                    rus_path: 1.4.treble
            
                                balance:
                                    type: num
                                    rus_path: 1.4.balance
            
                                turnonvolume:
                                    type: num
                                    rus_path: 1.4.turnonvolume
            
            ​
                                source:
                                    type: num
                                    rus_path: 1.4.currentsource
            
                                mute:
                                    type: bool
                                    rus_path: 1.4.mute
            
                                channelup:
                                    type: bool
                                    rus_path: 1.4.channelup
                                    knx_dpt: 1
                                    knx_listen: 6/0/8
                                    enforce_updates: 'true'
            
                                loudness:
                                    type: bool
                                    rus_path: 1.4.loudness
            
                                partymode:
                                    type: str
                                    rus_path: 1.4.partymode
            
                                donotdisturb:
                                    type: str
                                    rus_path: 1.4.donotdisturb
            ​
            und hier mein übernommenes plugin file

            Code:
            russound:
                class_name: Russound
                class_path: plugins.russound
                host: 192.168.33.xxx
                port: 9621
            ​
            Zuletzt geändert von bmx; 17.11.2024, 13:50.

            Kommentar


              #7
              Morg

              Kommentar


                #8
                Für mich sieht die Fehlermeldung so aus als ob Dein Plugin was Du aktuell nutzt nicht mit dem Core zusammenpasst (lib.network) Der von Dir verlinkte Thread hat allerdings so viele verschiedene Links auf Docker Images das ich jetzt nicht dabeigehen werde und die alle ausprobiere.
                Es läuft darauf hinaus das Du eine Änderung am Russound Plugin brauchst. Daher muss ich schon genaue Versionen von Core und Plugins haben damit ich weiß wo ich suchen muss.
                Ich muss allerdings auch zugeben das ich von Docker im SHNG Umfeld nicht allzuviel halte weil es ein flexibles System in einen vorgefertigten Baukasten presst das ich austauschen muss wenn ich was dran ändern will. Oder aber ich schaffe mir wieder zusätzliche Layer und dann kann ich es auch gleich in eine VM packen oder nativ betreiben...

                Kommentar


                  #9
                  HI bmx

                  vielen Dank für Dein Feedback


                  Das sind die Versionen:

                  SmartHomeNG Version: v1.9.3-master (manual)

                  SmartHomeNG Plugins Version:v1.9.3-master (manual)

                  Administrations-Oberfläche:shngAdmin v0.6.0​

                  Python Version: 3.9.15

                  Russound Plugin Version: 1.7.1

                  Betriebssystem Debian GNU/Linux 11 (bullseye)


                  Mein Dockerfile
                  Code:
                  FROM jentz1986/smarthomeng
                  
                  # Create symbolic link
                  RUN ln -s /usr/local/smarthome/plugins-default /mnt/plugins
                  
                  # Install pip and other system dependencies
                  RUN apt-get update && \
                  apt-get install -y python3-pip && \
                  rm -rf /var/lib/apt/lists/*
                  
                  # Install Python packages using pip
                  RUN pip3 install pye3dc pyttsx3 gpiozero beautifulsoup4
                  
                  # Copy the sync script to the container
                  COPY sync_plugins.sh /usr/local/bin/sync_plugins.sh
                  RUN chmod +x /usr/local/bin/sync_plugins.sh​

                  Mein docker compose
                  Code:
                  services:
                    shng:
                      build:
                        context: .
                        dockerfile: Dockerfile
                      restart: "unless-stopped"
                      volumes:
                        - ./volumes:/mnt
                      network_mode: "host"
                  
                    smartvisu:
                      image: richarvey/nginx-php-fpm:1.10.3
                      restart: "unless-stopped"
                      depends_on:
                        - shng
                      volumes:
                        - ./volumes/html:/var/www/html/
                      network_mode: "host"
                  
                    knxd:
                      image: welteki/knxd:latest
                      container_name: knxd
                      user: "1000:1000"
                      command: knxd /knxd/knxd.ini
                      volumes:
                        - ./volumes/knxd:/knxd
                      restart: always
                      depends_on:
                        - shng
                      network_mode: "host"​

                  Ich hoffe das macht es einfacher zu sehen was im core nicht mit dem russound plugin hackt.

                  Kommentar


                    #10
                    Schau bitte mal in deiner lib.network in der Klasse `Tcp_Client`, Methode `__receive_thread_worker()`, aktuelle Version Zeilen 883-900:

                    ```
                    if self.terminator:
                    __buffer += msg
                    while True:
                    # terminator = int means fixed size chunks
                    if isinstance(self.terminator, int):
                    i = self.terminator
                    if i > len(__buffer):
                    break
                    # terminator is str or bytes means search for it
                    else:
                    i = __buffer.find(self.terminator)
                    if i == -1:
                    break
                    i += len(self.terminator)
                    line = __buffer[:i]
                    __buffer = __buffer[i:]
                    if self._data_received_callback is not None:
                    try:
                    self._data_received_callback(self, line if self._binary else str(line, 'utf-8').strip())
                    ```

                    Interessant ist hier die letzte Zeile (900), könnte bei dir etwas anders aussehen. (die mit `self._data_received_callback()`)

                    Dito im russound-Plugin die Definition von `found_terminator`.

                    Von der aktuellen lib.network werden zwei (sichtbare) Parameter übergeben (self = die Tcp_Client-Instanz und der eigentliche Inhalt); im aktuellen russound-Plugin wird aber nur ein (sichtbarer) Parameter übergeben, nämlich `resp`, die Antwort aus dem Netz.
                    Die Zeile in russound müsste lauten:

                    `def found_terminator(self, client, resp):`, dann sollte es ohne weitere Änderungen laufen. Wenn die lib.network anders aussieht als oben passt noch irgendwas anderes nicht zusammen...

                    Update: der Code der lib.network ist an der Stelle seit drei Jahren unverändert, also sollte es daran eigentlich nicht liegen

                    Vielleicht hat jemand das `callback(self, data)` falsch interpretiert... ;-)

                    Kommentar


                      #11
                      Hey Morg,

                      herzlichen Dank für Dein Feedback.

                      Bin erst jetzt wieder im Lande und habe mir das Mal angeschaut.

                      In der Docker Version ist die komplette __receive_thread_worker() Methode von Zeile 779-881

                      Der genannte Ausschnitt in lib.network ist identisch mit meinem docker Zeile 815-835
                      Code:
                                                  if self.terminator:
                                                      __buffer += msg
                                                      while True:
                                                          # terminator = int means fixed size chunks
                                                          if isinstance(self.terminator, int):
                                                              i = self.terminator
                                                              if i > len(__buffer):
                                                                  break
                                                          # terminator is str or bytes means search for it
                                                          else:
                                                              i = __buffer.find(self.terminator)
                                                              if i == -1:
                                                                  break
                                                              i += len(self.terminator)
                                                          line = __buffer[:i]
                                                          __buffer = __buffer[i:]
                                                          if self._data_received_callback is not None:
                                                              try:
                                                                  self._data_received_callback(self, line if self._binary else str(line, 'utf-8').strip())
                                                              except Exception as iex:
                                                                  self._log_exception(iex, f'lib.network {self._id} receive in terminator mode calling data_received_callback {self._data_received_callback} failed: {iex}')​

                      Komplette Methode in lib.network:
                      Code:
                       def __receive_thread_worker(self):
                              """
                              Thread worker to handle receiving.
                              """
                              self.logger.debug(f'{self._id} started receive thread')
                              waitobj = IOWait()
                              waitobj.watch(self._socket, read=True)
                              __buffer = b''
                              self._is_receiving = True
                              if self._receiving_callback:
                                  self._receiving_callback(self)
                              # try to find possible "hidden" errors
                              try:
                                  while self._is_connected and self.__running:
                                      events = waitobj.wait(1000)     # BMX
                                      for fileno, read, write in events:  # BMX
                                          if read:
                                              timeout = False
                                              try:
                                                  msg = self._socket.recv(4096)
                                              except TimeoutError:
                                                  msg = None
                                                  timeout = True
                                              # Check if incoming message is not empty
                                              if msg:
                                                  # TODO: doing this breaks line separation if multiple lines
                                                  #       are read at a time, the next loop can't split it
                                                  #       because line endings are missing
                                                  #       find out reason for this operation...
                                                  # # If we transfer in text mode decode message to string
                                                  # # if not self._binary:
                                                  # #     msg = str.rstrip(str(msg, 'utf-8')).encode('utf-8')
                                                  # If we work in line mode (with a terminator) slice buffer into single chunks based on terminator
                                                  if self.terminator:
                                                      __buffer += msg
                                                      while True:
                                                          # terminator = int means fixed size chunks
                                                          if isinstance(self.terminator, int):
                                                              i = self.terminator
                                                              if i > len(__buffer):
                                                                  break
                                                          # terminator is str or bytes means search for it
                                                          else:
                                                              i = __buffer.find(self.terminator)
                                                              if i == -1:
                                                                  break
                                                              i += len(self.terminator)
                                                          line = __buffer[:i]
                                                          __buffer = __buffer[i:]
                                                          if self._data_received_callback is not None:
                                                              try:
                                                                  self._data_received_callback(self, line if self._binary else str(line, 'utf-8').strip())
                                                              except Exception as iex:
                                                                  self._log_exception(iex, f'lib.network {self._id} receive in terminator mode calling data_received_callback {self._data_received_callback} failed: {iex}')
                                                  # If not in terminator mode just forward what we received
                                                  else:
                                                      if self._data_received_callback is not None:
                                                          try:
                                                              self._data_received_callback(self, msg)
                                                          except Exception as iex:
                                                              self._log_exception(iex, f'lib.network {self._id} calling data_received_callback {self._data_received_callback} failed: {iex}')
                                              # If empty peer has closed the connection
                                              else:
                                                  if self.__running:
                                                      self._is_receiving = False
                                                      self._is_connected = False
                                                      try:
                                                          self._socket.shutdown()
                                                      except Exception:
                                                          pass
                                                      if timeout:
                                                          # TimeoutError exception caught
                                                          self.logger.warning(f'{self._id} connection timed out, disconnecting.')
                                                      else:
                                                          # default state, peer closed connection
                                                          self.logger.warning(f'{self._id} connection closed by peer')
                                                      waitobj.unwatch(self._socket)
                                                      if self._disconnected_callback is not None:
                                                          try:
                                                              self._disconnected_callback(self)
                                                          except Exception as iex:
                                                              self._log_exception(iex, f'lib.network {self._id} calling disconnected_callback {self._disconnected_callback} failed: {iex}')
                                                      if self._autoreconnect:
                                                          self.logger.debug(f'{self._id} autoreconnect enabled')
                                                          self.connect()
                                                      if self._is_connected:
                                                          self.logger.debug('{self._id} set read watch on socket again')
                                                          waitobj.watch(self._socket, read=True)
                                                  else:
                                                      # socket shut down by self.close, no error
                                                      self.logger.debug('{self._id} connection shut down by call to close method')
                                                      return
                              except Exception as ex:
                                  if not self.__running:
                                      self.logger.debug('{self._id} receive thread shutting down')
                                      self._is_receiving = False
                                      return
                                  else:
                                      self._log_exception(ex, f'lib.network {self._id} receive thread died with unexpected error: {ex}. Go tell...')
                              self._is_receiving = False​
                      hier der Abschnitt aus dem Russound plugin

                      Code:
                          def found_terminator(self, resp):
                              try:
                                  resp = resp.decode()
                              except Exception as e:
                                  self.logger.error("found_terminator: exception in decode: {}".format(e))
                                  return
                              try:
                                  self.logger.debug("Parse response: {0}".format(resp))
                                  if resp[0] == 'S':
                                      return
                                  if resp[0] == 'E':
                                      self.logger.debug("Received response error: {0}".format(resp))
                                  elif resp[0] == 'N':
                                      resp = resp[2:]
                                      if resp[0] == 'C':
                                          resp = resp.split('.', 2)
                                          c = int(resp[0][2])
                                          z = int(resp[1][2])
                                          resp = resp[2]
                                          cmd = resp.split('=')[0].lower()
                                          value = resp.split('"')[1]
                                          path = '{0}.{1}.{2}'.format(c, z, cmd)
                                          if path in list(self.params.keys()):
                                              self.params[path]['item'](
                                                  self._decode(cmd, value), self.get_shortname())
                                      elif resp.startswith('System.status'):
                                          return
                                      elif resp[0] == 'S':
                                          resp = resp.split('.', 1)
                      #                   s = int(resp[0][2])
                                          resp = resp[1]
                                          cmd = resp.split('=')[0].lower()
                                          value = resp.split('"')[1]
                      #                    if s in self.sources.keys():
                      #                        for child in self.sources[s]['item'].return_children():
                      #                            if str(child).lower() == cmd.lower():
                      #                                child(unicode(value, 'utf-8'), self.get_shortname())
                                          return
                              except Exception as e:
                                  self.logger.error(e)​
                      Ich habe nun die Funktion nach Deinem Kommentar hinzugefügt
                      Code:
                      def found_terminator(self, client, resp)
                      dies resultiert in diesen error log
                      Code:
                      2024-12-09  23:30:19 ERROR    plugins.russound    found_terminator: exception in decode: 'str' object has no attribute 'decode'
                      habe mir ein solchen Wert einmal ausspucken lassen
                      Client ist ein <lib.network.Tcp_client object at 0x7fc2102be2b0> und resp ist beim einschalten ein 'S' somit habe ich einfach die decode methode auskommentiert und voila ich habe diesen Error nicht

                      Code:
                      def found_terminator(self, resp):
                              try:
                                  resp = resp#.decode()
                              except Exception as e:
                                  self.logger.error("found_terminator: exception in decode: {}".format(e))
                                  return​
                      Ich bin mir aber noch nicht darüber bewusst was für Nebeneffekte dies mit sich bringt und warum ich bereits hier ein string erhalte.
                      Habe auch bemerkt das sh.(irgend_ein_item).audio.status(0) in meiner Logik nicht ausschaltet. Liegt aber wohl nicht an meinen Veränderung war bereits der Fall - volume source jedoch funktionieren einwandfrei -.-

                      Bin gespannt auf Dein Feedback

                      LG

                      Kommentar


                        #12
                        Du kannst den ganzen ersten try/except-Block löschen. Da der tcp_client bei dir nicht im binary-Modus läuft, liefert er schon fertige str zurück, und die Konvertierung (das decode) ist überflüssig.

                        Das sollte keine negativen Nebeneffekte haben

                        Habs im aktuellen develop angepasst.
                        Zuletzt geändert von Morg; 13.12.2024, 16:36.

                        Kommentar

                        Lädt...
                        X