Wenn dies dein erster Besuch hier ist, lies bitte zuerst die Hilfe - Häufig gestellte Fragen durch. Du musst dich vermutlich registrieren, bevor du Beiträge verfassen kannst. Klicke oben auf 'Registrieren', um den Registrierungsprozess zu starten. Du kannst auch jetzt schon Beiträge lesen. Suche dir einfach das Forum aus, das dich am meisten interessiert.
2019-11-17 22:05:50 INFO Main Loading '/usr/local/smarthome/plugins/kodi/plugin.yaml' to 'OrderedDict'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata paramlist = '['host', 'port', 'instance', 'autoreconnect', 'connect_retries', 'connect_cycle', 'send_retries']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata itemdeflist = '['kodi_item']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata plugin_functionlist = '['notify']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata get_plugin_function_defstrings -> '['notify(title, message, image, display_time)']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata get_plugin_function_defstrings -> '['notify(title:str, message:str, image:str, display_time:int)']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata get_plugin_function_defstrings -> '['notify(title, message, image=None, display_time=10000)']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata get_plugin_function_defstrings -> '['notify(title:str, message:str, image:str=None, display_time:int=10000)']'
2019-11-17 22:05:50 INFO Main plugin 'kodi': Metadata itemstructlist = '[]'
2019-11-17 22:05:50 INFO Main add_struct_definition: struct 'kodi.query' = OrderedDict([('name', 'Vorlage-Struktur für Kodi Infos, die nicht aktiv verämndert werden können.'), ('state', OrderedDict([('kodi_item@instance', 'state'), ('type', 'str'), ('visu_acl', 'ro')])), ('title', OrderedDict([('kodi_item@instance', 'title'), ('type', 'str'), ('visu_acl', 'ro')])), ('media', OrderedDict([('kodi_item@instance', 'media'), ('type', 'str'), ('visu_acl', 'ro')])), ('player', OrderedDict([('kodi_item@instance', 'player'), ('type', 'num'), ('visu_acl', 'rw'), ('enforce_updates', True)]))])
2019-11-17 22:05:50 INFO Main add_struct_definition: struct 'kodi.control' = OrderedDict([('name', 'Vorlage-Struktur für Kodi Befehle zum Steuern'), ('mute', OrderedDict([('kodi_item@instance', 'mute'), ('type', 'bool'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('volume', OrderedDict([('kodi_item@instance', 'volume'), ('type', 'num'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('speed', OrderedDict([('kodi_item@instance', 'speed'), ('type', 'num'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('on_off', OrderedDict([('kodi_item@instance', 'on_off'), ('enforce_updates', True), ('type', 'bool'), ('visu_acl', 'rw')])), ('input', OrderedDict([('kodi_item@instance', 'input'), ('type', 'str'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('favourites', OrderedDict([('kodi_item@instance', 'favourites'), ('type', 'str'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('macro', OrderedDict([('kodi_item@instance', 'macro'), ('type', 'str'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('audiostream', OrderedDict([('kodi_item@instance', 'audiostream'), ('type', 'foo'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('subtitle', OrderedDict([('kodi_item@instance', 'subtitle'), ('type', 'list'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('seek', OrderedDict([('kodi_item@instance', 'seek'), ('type', 'num'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('home', OrderedDict([('kodi_item@instance', 'home'), ('type', 'bool'), ('visu_acl', 'rw'), ('enforce_updates', True)])), ('stop', OrderedDict([('type', 'bool'), ('eval', "sh...input('stop')"), ('enforce_updates', True)])), ('play', OrderedDict([('type', 'bool'), ('eval', "sh...input('play')"), ('enforce_updates', True)])), ('pause', OrderedDict([('type', 'bool'), ('eval', "sh...input('pause')"), ('enforce_updates', True)])), ('previous', OrderedDict([('type', 'bool'), ('eval', "sh...input('skipprevious')"), ('enforce_updates', True)])), ('next', OrderedDict([('type', 'bool'), ('eval', "sh...input('skipnext')"), ('enforce_updates', True)])), ('select', OrderedDict([('type', 'bool'), ('eval', "sh...input('select')"), ('enforce_updates', True)])), ('contextmenu', OrderedDict([('type', 'str'), ('eval', "sh...input('contextmenu')"), ('enforce_updates', True)]))])
2019-11-17 22:05:50 INFO Main Loading '/usr/local/smarthome/plugins/kodi/locale.yaml' to 'dict'
2019-11-17 22:05:50 INFO Main plugin 'kodi': value not found in plugin configuration file for parameter 'port' -> using default value '9090' instead
2019-11-17 22:05:50 INFO Main plugin 'kodi': value not found in plugin configuration file for parameter 'instance' -> using default value '' instead
2019-11-17 22:05:50 INFO Main plugin 'kodi': value not found in plugin configuration file for parameter 'connect_retries' -> using default value '10' instead
2019-11-17 22:05:50 INFO Main plugin 'kodi': value not found in plugin configuration file for parameter 'connect_cycle' -> using default value '5' instead
2019-11-17 22:05:50 INFO Main plugin 'kodi': value not found in plugin configuration file for parameter 'send_retries' -> using default value '5' instead
2019-11-17 22:05:50 INFO Main Initialized plugin 'kodi' instance 'wohnzimmer' from from section 'kodi'
wobei ich auch schon versucht hätte die Instanz wegzulassen - die items dementsprechend die Instanz weggenommen - auch dort keine Verbindung möglich
Ports sind bei Kodi alle Standard belassen
Steuern mit KORE funktioniert einwandfrei - also auf der Kodi Seite hätte ich keine Fehler gesehen (wobei ichs nicht für unmöglich halte)
Du sprichst von 2 Instanzen - wie sehen da die Einträge zu beiden Instanzen in der plugin.yaml aus? Im Log steht, dass instance nicht gefunden wurde, aber bei der Initialisierung passt es dann doch wieder. Hier ist also am ehesten was mit deiner Config nicht sauber.
Was passiert denn nach der Initialisierung!? Logfile bitte.
Ich glaube, der Fehler liegt in der plugin.yaml des Plugins. Entferne dort bitte mal unter parameters den "instance" Eintrag. Der hat dort eigentlich nix verloren und wär für mich die Ursache des Übels!
das erste was mir auffällt: es gibt keine Items vom TYPE: INT. Es gibt nur Items vom TYPE: NUM
hab ich aus Unwissenheit der structs aus plugin/kodi/plugin.yaml kopiert (da ich nicht wusste dass instance sich anscheinend so überschreiben lässt)
Zu "Keine Route zum Zielrechner" -> schon mal probiert ob Du den Zielrechner pingen kannst? ich vermute der Netzwerkzugriff geht schlichtweg nicht.
ping funktioniert (von smarthomeng server), Kore (vom handy) funktioniert, Diverse Anzeigen im Plugin funktioniert nicht (title, volume, etc) - grundlegend dürfte aber eine Verbindung da sein, da play, pause, etc funktionieren
Ich glaube, der Fehler liegt in der plugin.yaml des Plugins. Entferne dort bitte mal unter parameters den "instance" Eintrag. Der hat dort eigentlich nix verloren und wär für mich die Ursache des Übels!
wobei mir dann völlig unklar ist wo ich definiere dass 192.168.1.81 das Wohnzimmer und 192.168.1.82 das Schlafzimmer ist
plugin.yaml schaut jetzt mal so aus - ohne instance krieg ich keine Verbindung (wenn das Item eine instance hat)
So, ich habe auf Hinweis von Onkelandy und anderen mal das Plugin etwas auf den Kopf gestellt. Dabei habe ich versucht, die Kompatibilität zu vorhandenen Installationen so weit wie möglich beizubehalten; in der Folge ist die Benennung von shng-Items und kodi-Items nicht mehr immer ganz schlüssig; mit der jetzt gelieferten plugin.yaml sollten aber (bis auf die Benennung des struct) alle bisherigen Funktionen weiter funktionieren.
Die Konfiguration wurde komplett überarbeitet, generalisiert und in eine externe Datei (commands.py) ausgelagert. Soweit es schreibende Kommandos angeht, kann das Plugin ausschließlich durch Änderungen an der commands.py erweitert werden. Für lesende Kommandos muss der Parsing-Code immer noch angefasst werden. Vielleicht schaffe ich es irgendwann auch noch, das zu generalisieren
Für die Entwicklungsreife: Es funktioniert, man kann es grundsätzlich mindestens so weit wie vorher nutzen. Es läuft - bei mir - stabil. Im Code habe ich insbesondere die Docstring-Einträge, die Aufteilung von Methoden und Eigenschaften in public und protected und die Metadaten inkl. Dokumentation überarbeitet.
Seid so lieb und testet das Plugin mal auf Herz und Nieren, und vor allem: schreibt mir, wenn Fehler auftreten. Im Debug-Modus ist das Plugin ziemlich gesprächig; oberhalb von Debug ist es ziemlich still. Bei der Prüfung von Konfigurationen erzeugt es hauptsächlich Error-Meldungen, weil es dann nicht lauffähig wäre.
Sachen, die ich noch prüfen/fixen muss:
Anzeige/Auswahl von Tonspur und Untertitel (wird als dict angezeigt, aber das Plugin nimmt an der Stelle kein dict an.)
Logging auf Ebene Info verbessern
Den Empfangs- und Verarbeitungscode verstehen und entknoten. (das Parsing bin ich schon angegangen, aber die Variante des Befehlsqueueings und der Entflechtung der Antworten braucht mehr Alkohol... )
Arbeiten mit mehreren Instanzen. Ging wohl vorher, konnte ich aber noch nicht testen (es gibt aber eigentlich keinen Grund, wieso es nicht mehr gehen sollte)
Notifications, insbesondere das automatisierte Aktualisieren von Wiedergabeinformationen (playtime usw). Geht derzeit nur über Trigger-Item 'update'; aufgrund von Wartezeiten auf die Antworten braucht ein Zyklus der Aktualisierung (getriggert) 2-3 Sekunden. Wenn Kodi das "automatisch" senden würde, könnte es problemlos im Sekundentakt laufen. Dann müsste ich die Items noch erweitern.
So, nach weiteren Testen und noch einigen Fehlerchen, die insbesondere in Bezug auf Audiostreams/Untertitel aufgetreten sind (der Rest ging ja gestern schon), habe ich nochmal "zurück auf Start" gespielt.
Ich habe regelmäßig Lockups gehabt und war nicht in der Lage festzustellen, wer an welcher Stelle welches Lock blockiert (und warum!). Mir schien auch die ganze Locking-Mimik etwas überzogen, wenn das Nachrichtenprotokoll im Kern auf Asynchronität ausgerichtet ist.
Das bis zu fünfmalige Wiederholen eines Kommandos, das schon beim ersten Mal einen Fehler verursacht hat, leuchtet mir überhaupt nicht ein. Wenn ein Fehler drin ist, muss ich den Fehler nicht nochmal senden.
Eine Zuordnung von Antworten zu Anfragen ist zwar ohne größeren Aufwand möglich, aber in diesem Zusammenhang nicht wirklich sinnvoll.
Also habe ich den Sende- und Empfangscode auch über der Haufen geworfen und neu geschrieben.
Beim Senden - getriggert durch update_item() oder aus einem Auswerteprozess (aktiver Player gefunden -> Infos abfragen) wird das Paket gebaut und in eine Queue gesteckt. (Die Queue stellt exklusiven Zugriff sicher, ist ein Python-Standardmodul). Am Ende der Sendefunktion wird versucht, alles zu senden, was in der Queue steckt. Wenn ich dabei konkurrierende Threads habe, ist das egal, weil durch das Queue-Objekt jedes Sendeobjekt nur einmal "verteilt" werden kann. Die Netzwerkebene sollte beim Sende und beim Empfänger ausreichend stabil sein, um ggf. auch gleichzeitige Verbindungen zu vertragen. Die gesendeten Kommandos werden in einer Liste gespeichert (ohne Locking).
Beim Empfangen - als Callback vom TCPConnection-Objekt - wird das Paket auf Plausibilität geprüft, in ein Objekt überführt, Fehler werden geloggt. Wenn es eine Antwort auf ein gesendetes Paket ist - nur dann enthält es eine MessageID - , dann wird das gesendete Paket aus der Liste der gesendeten Pakete gelöscht und als "erfolgreich" oder "Fehler" geloggt. Danach wird das Paket zur Auswertung abgegeben. Da jede Netzwerkverbindung einen eigenen Callback auslöst, kann jeder Callback seine eigenen Daten verarbeiten (lassen) und es bleibt nichts übrig, also brauche ich kein Locking und keine Queues.
Durch die Liste von gesendeten Nachrichten kann ich - mit einem bestimmten Zeitversatz - feststellen, ob Anfragen unbeantwortet geblieben sind. Das wird derzeit noch nicht ausgewertet.
Durch die Vergabe von eindeutigen MessageIDs könnte man weitere Nachverfolgung machen, ich sehe darin derzeit aber keinen Mehrwert.
Notifications (Statusmitteilungen des Players ohne vorherige Abfrage) werden über das Callback genauso bearbeitet wie alle anderen Anfragen. Solange parse_response() damit umgehen kann, kann alles verarbeitet werden.
Durch die - jetzt nochmal - neue Struktur lassen sich neue Funktionen über a) die commands.py auf Sendeseite und b) die Methode _parse_response() umsetzen; eine umfassende Anpassung von vielen Methoden (senden, empfangen, auswerten, Zählen, Status abfragen) entfällt.
Ich hoffe, dass damit zumindest mittelfristig die Nutz- und Wartbarkeit des Codes deutlich verbessert wurde. Ich werde heute noch weiter testen und ggf. Updates pushen, aber grundsätzlich funktioniert das schonmal alles.
Wie immer - bitte ausgiebig testen und insbesondere Fehler zurückmelden oder selber beheben...
Schöne Sache - eine solide Codebasis kann bestimmt dann auch für ähnliche Plugins herangezogen werden wie schon diskutiert.
Mehrfaches Senden eines Befehls... weiß auch nicht mehr genau, warum ich das gemacht hab. Bei einigen Geräten (avdevice) schien das Sinn zu machen. Nur weils das erste Mal nicht geklappt hat, heißt es ja nicht zwingend, dass es ein fehlerhafter Befehl war.
Das Nutzen der Message ID, um zu eruieren, ob der Befehl erfolgreich war oder nicht, fände ich praktisch, aber ist sicher nicht zwingend nötig.
Ich hab jetzt mal ein paar Sache probiert. Hat großteils gut funktioniert. Allerdings ist auch nach Stoppen eines Films der Player auf "1" geblieben, obgleich kein aktiver Player vorhanden. "Play" läuft dann ins Leere, kriegt man aber eigentlich nicht mit. Ob das schlimm ist, sei dahin gestellt.
Ich muss gestehen, dass ich durch die alte Logik nicht 100% durchgestiegen bin; aber soweit ich das nachvollziehen konnte, hat er bei einer Fehlermeldung den Befehl nochmal in die Queue gepackt, das ist natürlich Unsinn.
Mit MsgID zu prüfen, ob der Befehl erfolgreich war, ist jetzt schon drin, das Ergebnis (OK oder Fehler) wird auch geloggt. Die Liste der "offenen" Befehle wird entsprechend aktualisiert. Was momentan noch nicht passiert, ist alle Befehle älter als x Sekunden, die noch in der "offen"-Liste stehen, zu bearbeiten (nochmal senden, Fehler melden...); derzeit bleibt die Liste stehen und wird ggf. immer länger.
So, habe den Fehler mit "stop" gefixed (war mein Fehler - ich habe state auf "Stopped" gesetzt, dabei wird der aktive Player beendet, nicht in Status "stop" gesetzt).
Weiterhin habe ich mal versucht, einen Mechanismus zu implementieren, der "regelmäßig" unbeantwortete Kommandos prüft. Da ich für "regelmäßig" weder einen Scheduler noch eine Schleife bauen wollte, macht er das jedesmal, wenn er Daten empfangen hat, allerdings höchstens alle 'command_repeat'/2 Sekunden. Dazu hat er eine timer-Variable.
Wenn die Zeit überschritten ist (ohne Aktivität erfolgt auch keine Prüfung!), geht er alle gespeicherten und nicht beantworteten Kommandos durch und prüft jeweils, ob die schon oft genug wiederholt wurden. Falls ja, fliegen sie raus; falls nein, werden sie nochmal gesendet.
Da das Ereignis "nicht beantwortet" bei mir nicht aufgetaucht ist, ist das Testen schwierig. Ich habe also bei "Kommando beantwortet" es nicht aus der Liste löschen lassen, so dass diese sich immer füllt. Das wiederholte Senden und Hochzählen klappt, auch das Löschen beim Erreichen der maximalen Wiederholung. Wie sich das im "echten" Betrieb verhält, müssen wir mal beobachten.
Fazit derzeit: läuft, wirft keine Fehler, den Rest müssen wir testen. Wie immer - bitte um Feedback
So, nach noch einigen Arbeiten sollten wir jetzt einen guten und stabilen Stand erreicht haben. Neu- und Wiederverbindung klappt, Status wird entsprechend gelesen und gesetzt, und bis auf Netzwerkkabel ziehen kann das Plugin mit den meisten Problemen umgehen
Wer möchte, gerne mal testen. Ist derzeit noch im PR, demnächst hoffentlich in develop
Wir verarbeiten personenbezogene Daten über die Nutzer unserer Website mithilfe von Cookies und anderen Technologien, um unsere Dienste bereitzustellen. Weitere Informationen findest Du in unserer Datenschutzerklärung.
Indem Du unten auf "ICH stimme zu" klickst, stimmst Du unserer Datenschutzerklärung und unseren persönlichen Datenverarbeitungs- und Cookie-Praktiken zu, wie darin beschrieben. Du erkennst außerdem an, dass dieses Forum möglicherweise außerhalb Deines Landes gehostet wird und bist damit einverstanden, dass Deine Daten in dem Land, in dem dieses Forum gehostet wird, gesammelt, gespeichert und verarbeitet werden.
Kommentar