Snapcast Modul (WIP)

Begonnen von LeoSum, 28 Juni 2016, 08:37:26

Vorheriges Thema - Nächstes Thema

LeoSum

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

justme1968

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
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

CoolTux

Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

LeoSum

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

justme1968

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
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

CoolTux

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.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

LeoSum

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?

justme1968

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
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

LeoSum

Danke, dann hab ich erstmal wieder genug Beschäftigung!

Ich meld mich.

LeoSum

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

heig

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

CoolTux

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
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

CoolTux

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
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

LeoSum

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.

CoolTux

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
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net