Der Titel sagt es: Support für das Russound Plugin gibt es hier ...
Ankündigung
Einklappen
Keine Ankündigung bisher.
Support Thread für das Russound Plugin
Einklappen
X
-
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
-
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
-
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 logCode: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
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
-
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
-
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
-
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
-
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
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)
Code:def found_terminator(self, client, resp)
Code:2024-12-09 23:30:19 ERROR plugins.russound found_terminator: exception in decode: 'str' object has no attribute 'decode'
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
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
-
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.
- Likes 1
Kommentar
Kommentar