Hallo zusammen,
ich würde mich gerne darin versuchen ein Modul für die Multiroom-Audio Software Snapcast zu schreiben: https://github.com/badaix/snapcast
Durch das Querlesen von einer bereits vorhandenen python implementierung der Snapcast Steuerung (https://github.com/happyleavesaoc/python-snapcast) sowie der Dokumentation des Protokolls (https://github.com/badaix/snapcast/blob/master/doc/control_spec.md) habe ich bereits rausgefunden, dass hierzu Informationen im JSON Format via Telnet ausgetauscht werden.
Testweise habe ich mich mal über die Konsole per telnet mit dem Server Verbunden (er lauscht auf den Port 1705) und kann dort auch sehen, wenn ich etwas ändert (Lautsärke Änderungen, andere Stream zuweisung, etc.).
Allerdings komme ich nicht so recht dahinter, mit welcher Syntax ich selbst etwas versenden muss, um "set" commands auszuführen, geschweige denn das ganze aus dem Stehgreif in perl umzusetzen.
Ich habe nun in FHEM das Modul 70_JSONReadings.pm (https://forum.fhem.de/index.php/topic,38463.0.html) gefunden, welches zwar JSON ausliest, allerdings per HTTP und nicht per Telnet. Dort kann ich für mein Vorhaben also nicht abkupfern, oder?
Kann mich vielleicht jemand auf ein Modul hinweisen, welches bereits eine ähnliche Kommunikationsstuktur mit JSON über Telnet aufweist wie ich sie für Snapcast benötige? Leider ist mein Perl gar nicht gut und ich hoffe möglichst viel durch Abschreiben lernen zu können :)
Ich bin für alle Ideen oder Hinweise dankbar!
Gruß
Leo
Alles was Du machen musst ist Deine Daten die Du senden möchtest in JSON zu codieren und das dann per Telnet zu versenden. Anders rum hast Du ja schon festgestellt das Du Daten über Telnet erhalten kannst die JSON Format haben, diese musst du decodieren und dann als Reading bereit stellen.
Ich habe Dir nun bereits einige Dinge gegeben mit denen Du auf die Suche gehen kannst.
encode_json
decode_json
Für telnet schau Dir das Modul telnet an.
Für ein FHEM Modul gibt es im Wiki die Developer Seiten.
Ansonsten lerne intensiv Perl und wenn Du dann Probleme mit Code hast, poste ihn und umschreibe Dein Problem und wir werden helfen.
Grüße
nur kurz um zukünftige verwirrungen zu vermeiden: eigentlich hat das ganze nichts mit telnet zu tun. das verwendete protokoll nennt sich JSON-RPC.
es sind einfach json daten die über sockets ausgetauscht werden. es gibt diverse module die sockets verwenden um daten auszutauschen. das telnet modul gehört zwar auch dazu ist aber nicht das beste beispiel weil es vor allem verbindungen annimmt. du möchtest aber eine verbindung aufbauen.
schau dir mal das lightify modul an. das verwendet zwar kein json, ist aber ein recht einfaches beispiel wie man ein socket anlegt hinein schreibt und daraus liest. statt den binär daten verwendet du aber encode_json und decode_json.
gruss
andre
ps: das man telnet zum testen verwenden kann liegt daran das telnet auch nur ein protokoll über sockets ist und unter anderem mit klartext arbeitet.
Vielen Dank für eure Tipps!
Ich werde mir das ganze heute Abend mal ansehen und gucken was ich zustande bringe.
Und nicht darauf versteifen es unbedingt heute Abend zu schaffen. Das ist utopisch. Gerade ohne Perl Kenntnis. Das kann gerne auch Monate dauern, aber wenn Du es geschafft hast, weil Du es wirklich wolltest fühlt es sich super an ;D
Ich hab keine Zeit, das muss heute Abend fertig sein!
Spaß beiseite: Ich meinte dass ich heute Abend anfange ;)
Wo ich die Experten gerade so schön beisammen hab: gibt es einen sinnvollen Workflow zum Debuggen beim erstellen von fhem modulen? Nutzt ihr ein IDE?
Oder lädt man die nach jeder Korrekturschleife im Editor auf den Server und generiert sich temporäre Outputs in die Log-Datei?
Ich habe keine IDE, verwende einen normalen Editor mit Code und Syntaxhighlighting. Du kannst einzelne Funktionen oder Teile von Funktionen ohne FHEM Interner extern testen. Ansonsten schreibe ich den Code extern und lade die PM auf mein Entwickler FHEM. Dort teste ich dann mit print oder printf Debugausgaben.
Grüße
zum entwickeln sollte man nicht sein produktives fhem system verwenden sondern eine lokale ausgeheckte/installierte version auf dem entwicklungsrechner.
zum entwickeln selber reicht ein vi und Log ausgaben. da nichts kompiliert werden muss sondern der code (in fast allen fällen) einfach per reload neu geladen werden kann geht das auch sehr schnell.
gruss
andre
Entwickelst Du echt mit vi Andre? Ich finde den Editor wahnsinnig unflexibel und bin froh vim zu haben. Da kann man auch wundervoll unendlich viele Module/Plugins für laden.
Grüße
vim ist auch nur vi :)
gruss
andre
ich verwende als Testrechner meinen PC auf dem Opensuse drauf ist. Als Editor verwende ich KWrite.
Hat dies irgendwelche Nachteile, die ich übersehen habe, wenn fhem nicht unter /opt/fhem/ liegt und wie folgt gestartet wird?
ralf@linux-ralf4:~/prog/fhem-5.5> perl fhem.pl fhem.cfg
Gruß Ralf
Zitat von: justme1968 am 28 Juni 2016, 17:56:11
vim ist auch nur vi :)
gruss
andre
Naja so verallgemeinert stimmt das ja schon lange nicht mehr ;D
Ich sage da nur Richtungstasten und Entferntesten. Das alles funktioniert unter Standard vi ja nicht.
doch das funktioniert auch mit einem 'standard' vi. man muss nur die tasten richtig mappen.
den vi den du meinst gibt es schon lange nicht mehr. nachfolger war mal nvi. aber auch den gibt es nicht mehr.
vim ist inzwischen der standrard vi.
gruss
andre
Ach so? Ok. Das wusste ich nicht. Das erste was ich immer gemacht habe war entweder emerge vim oder apt-get vim. Naja, gut zu wissen.
Grüße
Hallo zusammen,
an einem Abend ist das tatsächlich nicht zu bewerkstelligen ;)
Mittlerweile bin ich so weit, dass ich JSON empfangen, senden und (de)-kodieren kann. Quick and dirty lassen sich so schon Befehle absetzen.
Aber ich hätte das ganze gerne sauber als Readings aufgelistet und aktualisiert in fhem um dann mit notifies auf Veränderungen reagieren zu können.
Wie setzt man das nun um? Dazu hab ich zwei Fragen:
1. Kann ich mit readingsSingleUpdate
beliebige Readings setzen oder müssen die vorher wie variablen angelegt werden?
2. Können Readings Unterreadings haben? Im Wiki http://www.fhemwiki.de/wiki/DevelopmentModuleIntro#Readings lese ich die Zeilen $hash{READINGS}{Temp}{VAL}
$hash{READINGS}{Temp}{TIME}
die auf soetwas hindeuten würden. Lässt sich das beliebig verlängern?
Konkret würde ich gerne eine solche Struktur erzeugen, und einzelne Felder entsprechend der empfangenen Daten aktualisieren:
- Server
- Client 1
- Volume
- Muted
- Value
- Mac-Adress
- Name
- StreamID
- ...
- Client 2
- Volume
- Muted
- Value
- Mac-Adress
- Name
- StreamID
- ...
- Client n
- ...
Fällt euch vielleicht ein Modul ein, was ähnliches tut das ich als Beispiel nehmen könnte?
Danke und viele Grüße
Leo
1. ja. wenn du mehrere readings auf ein mal setzt solltest du aber die begin/bulk/end variante verwenden.
2. nein. wie die readings intern abgelegt sind sollte dir auch egal sein. d.h. du solltest nicht direkt auf $hash-{READINGS} zugreifen.
du kannst eine hierarchie nur über den namen des readings abbilden. schau dir doch mal das jsonReadings modul an.
gruss
andre
Schau mal hier (http://www.fhemwiki.de/wiki/DevelopmentIntroduction) und da unter Readings
Danke für die Hinweise zu den Readings!
JSONREADINGS werde ich mir mal genauer ansehen.
Das Problem was ich sehe ist, dass ich eine sehr lange Liste an Readings erhalten werde, wo dann für jeden Client die gleichen Größen aufgelsistet werden (Beispielsweise Volume). Dann müsste jedes Reading einen Prefix im Namen erhalten der die Client-Zuordnung erlaubt (also bspw. Client1_Volume).
Mir ist aufgefallen, dass das Philips Hue Modul ja ein ähnliches Problem hat: Eine Bridge, viele Lampen. Hier wird dann für jede Lampe ein neues Device angelegt, dem die Readings zugeordnet werden. So könnte ich das ja auch machen. Ist das zufällig das, was im Wiki mit "zweistufigen Modulen" gemeint ist? Ist das für den Anfang wohl zu kompliziert?
Grüße
Leo
die menge an readings ist prinzipiell egal. es gibt devices mit sehr vielen readings.
aber wenn die readings sowieso zu unterschiedlichen logischen geräten gehören ist es sinnvoll sie auch in fhem auf mehrere devices aufzuteilen. genau das ist mit zweistufigen modulen gemeint. manchmal wird das auch logische und physikalische module genannt.
das ist nicht prinzipiell schwierig und mann muss auch nicht unbedingt zwei fhem module dafür verwenden. je nach geschmack kann das auch ein einziges modul sein das sich ein mal als zentrale und ein mal als devices ausgeben kann.
ob man das als ein oder zwei module implementiert hängt z.b. davon ab wie viel überschneidungen im code es gibt.
gruss
andre
Zitat von: LeoSum am 04 Juli 2016, 13:21:45
Mir ist aufgefallen, dass das Philips Hue Modul ja ein ähnliches Problem hat: Eine Bridge, viele Lampen. Hier wird dann für jede Lampe ein neues Device angelegt, dem die Readings zugeordnet werden. So könnte ich das ja auch machen. Ist das zufällig das, was im Wiki mit "zweistufigen Modulen" gemeint ist? Ist das für den Anfang wohl zu kompliziert?
Jepp genau das ist es. Ein zweistufiges Modul. Das erste für die Schnittstelle/Treiber das zweite für die einzelnen Devices.
Ah ok! Ich glaube ich würde das erstmal in einem Modul behalten und nach dem ersten Define die Namen der Clients rausfinden.
Damit dann für jeden Client ein Gerät anlegen.
Und wie genau lege ich diese dann an? Kann ich quasi den define Befehl aus dem Modul heraus nochmal aufrufen?
es gibt einen offiziellen weg über autocreate.
beispiel dafür findest du z.b. im 36_LaCrosse modul. dazu muss die parseFn UNDEFINED und den inhalt des deines zurück geben.
oder du rufst CommandDefine auf. beispiel z.b. in 30_HUEBridge
autocreate gibt dem user mehr einflussmöglichkeiten.
selber CommandDefine aufrufen gibt dem modul mehr einfluss und ist für den anfang vielleicht etwas einfacher.
gruss
andre
Danke, dann hab ich erstmal wieder genug Beschäftigung!
Ich meld mich.
Hallo Zusammen,
ich habe mittlerweile ein bisschen was zusammengedengelt, was soweit auch läuft.
Aber leider nur bis zum nächsten Neustart von fhem.
Irgendwie ist der socket danach nicht mehr erreichbar bis ich die devices lösche und neu define.
Hat vielleicht jemand Lust mal über meinen Code zu schauen und mir zu Zeigen woran das liegen könnte? Das wäre prima! :)
Meine Priorität liegt zwar erstmal auf einem zuverlässig funktionierenden Modul, aber wenn euch beim Lesen was auffällt, spart bitte nicht mit Kritik!
Ich will ja was lernen ;)
Danke und Gruß
Leo
Hi, ich habe Snapcast jetzt auch schon einige Zeit im Einsatz (in Verbindung mit Mopidy als Quelle) und finde das System wirklich gut - die Frage nach einem FHEM Modul habe ich mir auch schonmal gestellt. Da ich aber nicht so der Entwickler bin, bin ich froh, dass du dich dran gemacht hast!
Wenn ich dich mit Testing o.ä. Unterstützen kann, sag gern bescheid.
Viele Grüße
Hallo Leo,
Sorry, ich wollte mir das ganze anschauen und habe es dann vergessen. Ich hoffe das ich heute Abend einmal über Dein Modul schauen kann. Leider kann ich mir nur den Code an sehen aber keine Funktionalität prüfen da ich kein snapcast habe.
Grüße
Leon
Hallo Leo,
Ich habe mir mal Deinen Code angeschaut. Du bist ja wirklich ziemlich weit gekommen. Gratuliere. Gibt da so ein zwei Sachen die ich nicht verstehe, habe aber leider auch kein snapcast, kann daher nicht testen.
Wie genau funktioniert Dein Modul. Es gibt keine Beschreibung. Du hast set Befehle drin aber diese sind unter den Attributen zu finden, ich kann aber nichts finden um die Befehle in der Tat als set DEVICE mute zu setzen.
Grüße
Hallo zusammen,
war jetzt zwei Wochen im Urlaub, daher meld ich mich erst jetzt zurück.
heig, danke für dein Angebot fürs Testen! Ich habe mein Modul jetzt seit einiger Zeit bei mir im Einsatz und für meine Zwecke funktioniert es einwandfrei. Wenn ich Zeit finde, schreibe ich mal eine Beschreibung und dann kannst du es testen.
Leon, danke fürs drüberlesen und die Blumen! ;)
Das mit dem setten ist genau meine größte Baustelle:
Ich wollte ja aus Komplexitätsgründen keine zwei Module schreiben. Daher wird nur der Server als Snapcast device definiert und direkt nach dem definieren eine Anfrage geschickt, welche Clients der Server kennt (aufruf der Funktion "Snapcast_Update_Devices" in Zeile 118).
Darauf hin schickt der Server eine große JSON nachricht über den Socket und daraus liest das Modul dann alle Clients und deren Settings (Zeile 184). Jeder Client wird nun als Dummy device angelegt (Zeile 218), da ich nicht weiß wie ich wirkliche Snapclient devices erstelle.
Wann immer sich etwas bei einem Client ändert (Lautsärke, Stream, Mute-State, ...) schickt der Server von sich aus eine Nachricht über den Socket und die Readings des jeweiligen Dummys werden aktualisiert (die toReadings Funktion hatte ich von JSONReadings geklaut).
Und da ich zum setten ja etwas an den Snapserver schicken muss und nicht direkt mit den clients (also Dummys) spreche, ist mir nix besseres eingefallen als das ganze über das Fake-Attribut "SetClient" des Servers zu machen. Das Value dahinter setzt sich dann aus Client-Mac-Adresse, Befehl und Wert zusammen (z.B. attr Snapcast SetClient b827eba67a7b_mute_1 oder attr Snapcast SetClient b827eba67a7b_volume_50) und wird dann entsprechend wieder zerlegt (Zeile 269), in eine valide JSON Nachricht übersetzt und dann über den Socket geschickt (ab Zeile 300).
Ist das nachvollziehbar?
Wenn du mir ein Beispiel zeigen kannst, wie das richtig gemacht wird, wär das natürlich klasse!
Der Umweg über attr hat den Nachteil dass ich das attribut immer manuell wieder löschen muss, weil es sonst beim nächsten Neustart ein Problem gibt wenn das noch gesetzt ist.
Hallo Leo,
Ok ich denke ich sehe da nun etwas mehr durch. Es gibt nun für Dich zwei Wege das ganze etwas schicker zu machen.
Soweit ich das gelesen habe gibt es ja wohl auch schon den ein oder anderen User.
1. Du machst daraus ein 2 stufiges Modul. Das wäre die beste und sauberste Lösung. Und ich denke mal da willst Du ja eh am Ende hin.
2. Wenn Du es erstmal nur ein bisschen besser machen willst sorgst Du dafür das Dein Modul einmal das Serverdevice an legt und dann die Clientdevices. Eigentlich so wie jetzt schon aber ohne Dummy. Statt beim anlegen der Clientdevices das Modul Dummy zu nehmen nimmst Du Dein Modul. Daher musst Du im Define Teil eine Unterscheidung treffen können.
Hier habe ich gesehen das Du in der Clientroutine ->{host}{mac} aus liest. Da bedeutet für mich das Dein Server als JSON String den hostnamen des clients und die mac zurück gibt. Hier kannst Du an setzen.
Die Unterschiedung erfolgt auf Basis der mitgegebenen Optionen.
Server
define <nam> Snapcast <host>
Client aufruf der define Zeile erfolgt über die Routine
CommandDefine(undef,$devname Snapcast $key->{host} $key->{host}{mac});
return (@a != 3) or (@a != 4);
Und dann kannst Du im define Teil eine Bedingung einbauen und unterhalb der jeweiligen Bedingung dann für Server und Client die Attribute setzen.
Ausserdem machst Du ein INTERNAL für den Client. Zum Beispiel mac, auf Basis dieses Internals kannst Du dann sagen zeige die set Befehle nur bei Devices welches das Internal mac haben.
Schreib mal zu was Du Dich entscheidest. Beim zweiten kann ich Dir gerne etwas helfen.
Grüße
Hallo Leon,
danke für die ausführliche Beschreibung! Mir ist jetzt klar wie das funktionieren sollte.
Ich werde wohl Variante 2 verfolgen. Wenn ich das mit dem setten hinbekommen habe, setze ich mich auch mal hin und schreibe eine Erläuterung mit Beispielen, sodass auch andere das Modul benutzen können.
Allerdings werde ich damit frühestens nächste Woche anfangen. Anstelle von Multi-Room Audio gibts für mich jetzt erstmal eine Woche Open-Air Audio :)
Ich melde mich.
Guten Abend,
ich suchte gerade nach einem Snapcast Modul für Fhem und bin dann über dieses Thema hier gestolpert.
Gibt es schon Neuigkeiten zu dem Modul? Bzw. kann man die Entwicklung irgendwie unterstützen?
Schöne Grüße,
Steffen
Ich bin mal so frei das Thema nochmal raufzuholen da ich auch sehr an einem FHEM Modul von snapcast interessiert bin.
Stelle mich gerne als Tester zur Verfuegung :)
Da ich momentan nicht dazu komme die set Methode richtig umzusetzen, habe ich meinen aktuellen Stand mal mit ein paar Erklärungen bei Github hochgeladen:
https://github.com/LeoSum8/snapcast-fhem/tree/master
Um rege Mitarbeit via Pull-Requests oder Forks wird gebeten! :)
Gäbe es eine sinnvolle Möglichkeit die vorhandene Python Library zu verwenden?
Ich denke du meinst diese hier?
https://github.com/happyleavesaoc/python-snapcast
daran hatte ich mich orientiert um die richtigen JSON Nachrichten zusammenzubauen und zu senden, bzw. zu empfangen und zu zerlegen. Der Teil des Moduls klappt soweit.
Was ich noch nicht vollständig umgesetzt habe ist, die Signale an FHEM in Perl richtig einzubinden und einzelne Clients richtig als Devices anzulegen.
Welchen Vorteil siehst du darin, die python library zu verwenden?
Mittlerweile gibt es hier von einem talentierteren Programmierer ein weiteres Modul:
https://github.com/unimatrix27/fhemmodules/blob/master/96_Snapcast.pm
Auf den ersten Blick scheint er es besser hinbekommen zu haben. Ich bin aber noch nicht zum Testen gekommen.