zweistufiges Modul - Broadcast an logische Module

Begonnen von bugster_de, 20 Januar 2014, 08:42:38

Vorheriges Thema - Nächstes Thema

bugster_de

Hi,

das ist sicherlich nicht die klassische Anfängerfrage.

Ich habe ein zweistufiges Modul zur Unterstützung der Squeezebox geschrieben (siehe Forum Multimedia). Dabei gibt es ein physikalisches Device, welches die Verbindung zum SB-Server handelt sowie beliebig viele logische Devices, welche die am SB-Server angeschlossenen Player represäntieren.

Nun benötige ich eine Möglichkeit, dass das physikalische Device alle logisches Devices, die mit ihm verbunden sind benachrichtigt. Sprich ein Broadcast vom physikalischen an alle logischen Devices.
Wie mache ich das?

Wenn ich die Dispatch Funktion aufrufe, dann sollte ich ja eine ID mitgeben, damit das logische Modul entscheiden kann, ob es gemeint ist.
Nun habe ich mit Dispatch rumgespielt und an Stelle der ID der Dispatch einfach "BROADCAST" mit auf den Weg gegeben. Die logischen Geräte können dies in der ParseFn erkennen, aber da ich ja keine ID mitgegeben habe, kann das logische Gerät seinen eignenen hash nicht heraus findenes sei denn ich gehe mit einer Loop über alles in der $defs durch. Das erzeugt aber kräftig Rechenlast, was ich vermeiden will.

bevor jemand fragt: Anwendungsfall ist wie folgt:
- der Squeezebox Server wird vom Anwender händisch ausgeschaltet
- das logische device in FHEM erkennt dieses
- das logische Device broadcastet 'SERVER OFF' an alle physikalischen Devices, welche dann ihren state auf off setzen (denn ein Player ohne Server nutzt ja nichts)
- der Anwender kann dann auf dieses off am Player mit einem notify reagieren udn evtl. angeschlossene verstärker etc. ausschalten

Umgekehrt:
- der SB-Server wird wieder eingeschaltet
- das physikalische Modul erkennt dieses und sendet Broadcast "SERVER ON" an alle logischen Devices
- die logischen devices können darauf reagieren und überprüfen, ob ihr jeweiliger Player ebenfalls eingeschaltet ist (denn nur weil der Server läuft heisst es nicht zwingend, dass auch die Player laufen)
- falls der jeweilige Player eingeschaltet ist, dann kann per notify auch angeschlossene verstärker eingeschaltet werden.

justme1968

#1
Dispatch ruft ParseFn jeweils mit dem $hash des devices IODev auf. also des device das die nachricht empfangen hat. nicht das device für das die nachricht bestimmt ist. und auch nicht für jede instanz sondern nur ein mal.

deine ParseFn ist dafür zuständig die  nachricht an das oder die zuständigen logischen devices zu verteilen. wie genau bleibt dir überlassen. normalerweise wird ParseFn direkt die readings im logischen device setzen. genau so gut kannst du aber auch die SetFn eines bestimmten logischen devices aufrufen. oder jede andere funktion und nicht nur für ein device sondern für meherere oder alle.

eine schleife über alle devices des gleichen typs schaut so aus:    foreach my $d (sort keys %defs) {
       next if ($defs{$d} ne $hash->{TYPE} );
       xxx_MachWas{$defs{$d}};
     }


ob du in der schleife jetzt eine funktion für jedes device aufrufst oder direkt das reading für jedes device setzt ist dir überlassen. ob du z.b. die normale SetFn aufrufst oder eine extra funktion hast bleibt auch dir überlassen. du kannst in der schleife neben TYPE natürlich noch weitere bedingungen prüfen. z.b. ob das IODev gleich dem eigenen device ist. wenn du den defptr mechanismus verwendest kannst du statt über alle devices zu iterieren nur über alle defptr deines typs iterieren.

wenn du alles an readings fest machen kannst dann kannst du auch einfach NotifyFn in den logischen devices dazu verwenden. wenn jedes logische device NOTIFYDEV auf das server (also das eigene IODev) device setzt wird ganz automatisch deine NotifyFn für jede readings änderung im server aufgerufen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

bugster_de

hi,

Danke !

Ich habe das in der ReadFn des physikalischen Device mal so animplementiert wie von Dir beschrieben und auch auf IODev geprüft. Das geht soweit wohl auch, fand ich aber unschön.

Die Notify Funktion scheint aber genau das zu sein, was ich suche, denn ich setze am physikalischen Device ein Reading, auf das dann die logischen reagieren können. das probiere ich mal !

viele grüße

bugster_de

Hi,

so nun hab ich es umgesetzt. Allerdings finde ich die NotifyFn nicht geschickt, da die ja dann auch bei jedem FHEM Event ausgelöst wird und entsprechend Rechenlast erzeugt, nur um fest zu stellen, dass sie nicht gemeint ist.

Ich loope jetzt also durch die %defs, um die Instanzen der logischen Module zu finden, die mit dem physikalischen verbunden sind und rufe dann dort eine dedizierte Funktion für den Broadcast auf.

Für alle die es interessiert:
sub SB_SERVER_Broadcast( $$$ ) {
    my( $hash, $cmd, $msg ) = @_;
    my $name = $hash->{NAME};
    my $iodevhash;

    foreach my $mydev ( keys %defs ) {
# the hash to the IODev as defined at the client
if( defined( $defs{$mydev}{IODev} ) ) {
    $iodevhash = $defs{$mydev}{IODev};
} else {
    $iodevhash = undef;
}

if( defined( $iodevhash ) ) {
    if( ( defined( $defs{$mydev}{TYPE} ) ) &&
( defined( $iodevhash->{NAME} ) ) ){

if( ( $defs{$mydev}{TYPE} eq "SB_PLAYER" ) &&
    ( $iodevhash->{NAME} eq $name ) ) {
    # we found a valid entry
    my $clienthash = $defs{$mydev};
    my $namebuf = $clienthash->{NAME};
   
    SB_PLAYER_RecBroadcast( $clienthash, $cmd, $msg );
}
    }
}
    }
   
    return;
}


Die ganzen defined() Abfragen benötigt man wohl, da sonst 01_FHEMWEB abschmiert. Wurde aber glaube ich mal in einem Thread besprochen. Hat sich mir aber nicht erschlossen, warum das so ist.

justme1968

wenn du nur interess an den events von einem device (deinem server) hast kannst du NOTIFYDEV setzen. dann wird NotifyFn nur noch für diese events aufgerufen.

das defined ist nötig weil der zugriff auf einen nicht existierendes hash element dieses automatisch mir dem wert undef anlegt und du so dann unvollständige devices erzeugst.

die Schleife über alle devices sollte noch etwas effizienter (und übersichtlicher) werden wenn du darin nicht if/else verschachtelst sondern direkt mit 'next if (...);' zum nächsten element springst wenn das aktuelle die bedingungen nicht erfüllt.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

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

bugster_de

Hi,

ah, das mit dem anlegen als undef war mir nicht klar. Man lernt nie aus :-)

Die next if Abfrage ist eine gute Idee. Bei größeren FHEM Installationen auf schmalen Servern nimmt die Loop sonst zuviel Performance weg.

Danke !