[gelöst] Sind alle(!) userReadings als Ergebnis einer(1) Perl-Funktion möglich?

Begonnen von th0masrad, 06 Dezember 2023, 18:29:20

Vorheriges Thema - Nächstes Thema

th0masrad

Ich nutze in meinen Devices die userReadings sehr intensiv; soll heißen, meine Devices haben sehr viele userReadings. :)
Binde ich ein neues Gerät von bekanntem Typ ein, kopiere ich die userReadings von einem alten auf das neue Device per copy & paste oder dem copy-Befehl.

Aber wenn ich Änderungen an der Menge der userReadings, z.B. eine Neues dazu oder ein Veraltetes weg, mache, durchsuche ich händisch alle Devices des betroffenen Typs und ändere durch Löschen oder Hinzufügen. Dabei sind mir schon viele Fehler unterlaufen.

Cool wäre nun, wenn alle userReadings eines Device-Typs mittels Perl generiert werden könnten. Dann könnte ich statt der Einzelaufzählung
attr Device userReadings uRA {perl_funktion_A()},\
userReadings uRB {perl_funktion_B()},\
userReadings uRC {perl_funktion_C()},\
userReadings uRD {perl_funktion_D()},\
...
einen Typ ausrüsten:
attr Device_vom_Typ_A userReadings {perl_funktion_die_alle_userReadings_für_den_Typ_A_zurückgibt()}
Natürlich reicht mein Verständnis von Perl und den Strukturen in FHEM nicht aus, um
  • zu wissen, ob mein Wunsch überhaupt möglich ist und
  • wie der entsprechende Perl-Code aussieht.

Vielleicht gibt es auch eine andere Lösung, an die ich bisher nicht gedacht habe!

Mit freundlichen Grüßen
Th0mas Rad.

betateilchen

Kann man, würde ich aber dann nicht als userReading definieren, sondern über ein notify auf das gewünschte device, das die gewünschte perl-Funktion aufruft und in der mit setreading gearbeitet wird. Das ist übersichtlicher und auch nach längerer Zeit noch problemlos nachvollziehbar.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Torxgewinde

#2
Hi,
Was hältst du von folgenden Lösungsansätzen?

uRA {
    my $myLastLetter = $reading =~ s/.*(.)$/$1/r;
   
    return perl_funktion_A() if ($myLastLetter eq "A");
    return perl_funktion_B() if ($myLastLetter eq "B");
    return perl_funktion_C() if ($myLastLetter eq "C");
    return perl_funktion_D() if ($myLastLetter eq "D");
   
    return $myLastLetter;
},
uRB {
    my $myLastLetter = $reading =~ s/.*(.)$/$1/r;
    return perl_funktion($myLastLetter);
},
uRC {
    return perl_funktion($name, $reading);
}

Was man vermeiden sollte, ist mit ReadingsSingleUpdate oder BulkUpdate aus einem UserReading direkt weitere Readings für das eigene Device zu setzen. Das führt sehr schnell mal zu einer Rekursion... Mit einer Verzögerung kann man sich bei solchen Anforderungen behelfen, oder wenn man wirklich nur readingsBulkUpdate() aufruft (ohne readingsEndUpdate()):

define test dummy
attr test room Test
attr test setList test
attr test userReadings test {\
readingsBulkUpdate($hash, "another", "value");;\
readingsBulkUpdate($hash, "another2", "value2");;\
readingsBulkUpdate($hash, "another3", "value2");;\
readingsBulkUpdate($hash, "another4", "value2");;\
\
return "test";;\
}

Aber wie gesagt, das kann schnell mal in einer Rekursion enden, wenn man nicht aufpasst!

th0masrad

Zitat von: betateilchen am 06 Dezember 2023, 18:33:58... dann nicht als userReading definieren, sondern über ein notify auf das gewünschte device ...

Ich danke dir für deine Antwort; allerdings benötige ich noch etwas Hilfe, um sie zu durchdringen! :-[

Du meinst: Anstatt auf den userReadings-Mechanismus zu bauen, bilde ich ihn indirekt nach!?! Wenn also (in meinem Beispiel) auf meinem Device_vom_Typ_A ein Event einläuft, merkt das das notify und errechnet die "User"-Readings; verstanden. 8)

Spontan denke ich, dass nun für jedes Device ein eigenes notify angelegt werden muss. Zwar wäre ein notify, das auf den Device-Typ reagiert, ideal, aber mMn. kann notify nur Gerätenamen als Suchmuster verarbeiten.

Wenn ich damit richtig liege, ist deine Lösung zwar gut, hätte aber den "Nachteil", für jedes Device noch ein notify einzubauen zu müssen...


Zitat von: betateilchen am 06 Dezember 2023, 18:33:58Kann man, ...
Bezieht sich das auf einen zweiten Lösungsansatz? O:-)
Mit freundlichen Grüßen
Th0mas Rad.

th0masrad

#4
Zitat von: Torxgewinde am 08 Dezember 2023, 08:16:30Was hältst du von folgenden Lösungsansätzen?
Auch dir vielen Dank für deine Antwort!

Zitat von: Torxgewinde am 08 Dezember 2023, 08:16:30uRA {
    my $myLastLetter = $reading =~ s/.*(.)$/$1/r;
    ...
},
uRB {
    ...
},
uRC {
    ...
}
Der erste Ansatz enthält "leider" wieder die Aufzählung aller userReadings. Genau das wollte ich vermeiden, denn es besteht wieder die Gefahr, dass ich welche vergesse. ;)

Zitat von: Torxgewinde am 08 Dezember 2023, 08:16:30attr test userReadings test {\
    readingsBulkUpdate($hash, "another", "value");;\
        ...
    return "test";;\
}
Kann man verallgemeinert sagen, dass die Idee ist, nur genau ein (1) userReading, das alle anderen Readings setzt, zu haben? Ich glaube, das kommt einer Vorstellung sehr nahe! :D
Dieses eine userReading würde dann bei allen Devices des gleichen Typs eingesetzt werden können. Der einzige Nachteil wäre, dass es in den Devices dann ein "unnützes" Reading mit seinem Namen und irgendeinem Inhalt geben würde.

Das kann ich verschmerzen! :))
Mit freundlichen Grüßen
Th0mas Rad.

Beta-User

Zitat von: th0masrad am 08 Dezember 2023, 11:22:27Der erste Ansatz enthält "leider" wieder die Aufzählung aller userReadings. Genau das wollte ich vermeiden, denn es besteht wieder die Gefahr, dass ich welche vergesse. ;)
Beim "Nicht-Vergessen" könnte eventuell das Modul "archetype" helfen.

Das mit dem "einen (!) userReading" wird eher nicht klappen. Das widerspricht dem Design dieser Funktionalität, auf die Schleifengefahr war ja schon hingewiesen worden.

Generell _vermute_ ich, dass der Ansatz mit vielen userReadings (auch noch ohne trigger?) an sich ein Hinweis auf einen Design-Problem bei dir ist. Vielleicht magst du uns mit mehr Infos versorgen, welchem Zweck das dient?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

Torxgewinde

@th0masrad:
ZitatDer erste Ansatz enthält "leider" wieder die Aufzählung aller userReadings. Genau das wollte ich vermeiden, denn es besteht wieder die Gefahr, dass ich welche vergesse. ;)
Ja, da wäre die Möglichkeit mit dem eigenem Devicenamen (Variable $name) und aus dem Readingnamen (Variable $reading) eine zentral definierte Funktion zu füttern, die dann nur an einer Stelle gepflegt definiert ist (z.B. 99_myUtils). Das wäre dann wie bei dem UserReading uRC der Fall. Dieser Ansatz lässt sich mit der zweiten Methode kombinieren, dazu gleich.

ZitatKann man verallgemeinert sagen, dass die Idee ist, nur genau ein (1) userReading, das alle anderen Readings setzt, zu haben?
Genau, das fungiert dann als Trigger für deine Funktion. Das lässt sich üblicherweise auch mit einem DOIF oder NOTIFY machen, oder eben direkt am Device selbst mit solch einem UserReading.

Bei UserReadings kann man zudem auch noch einschränken, wann das UserReadings ausgeführt wird. Damit dort nicht unnütz immer gleiche Werte berechnet werden, sollte man üblicherweise einen Trigger definieren. Bei deinem Ansatz macht es aber ggf. Sinn einfach ohne Trigger zu arbeiten, oder man triggert auf die Eingaben mit denen deine Funktion eine Berechnung durchführt. Das lässt sich ohne Details nicht genau sagen, was bei dir jetzt sinnvoller wäre.

Ein Trigger auf Aktualisierung der Readings bla und blubb des eigenen Device wäre dann zum Beispiel (P.S: der RegEx könnte noch schärfer mit ^ und $ ergänzt werden, aber das wird unübersichtlich):
meinReadingDasWasMacht:(?:bla|blubb) { return "hallo"; }
Würdest du nur den CodeBlock vom UserReading triggern wollen, falls das Reading bla auf den Wert blubb gesetzt wird, wäre es ungefähr so:
meinReadingDasWasMacht:bla:.blubb { return "hallo"; }
ZitatDieses eine userReading würde dann bei allen Devices des gleichen Typs eingesetzt werden können. Der einzige Nachteil wäre, dass es in den Devices dann ein "unnützes" Reading mit seinem Namen und irgendeinem Inhalt geben würde.
Genau, warum auch nicht.


Das könnte dann insgesamt ungefähr so aussehen, wenn man deine initial Frage nochmal hinzunimmt (Demo):
defmod test dummy
attr test comment Test for: https://forum.fhem.de/index.php?topic=136152.msg1296151#msg1296151
attr test room Test
attr test setList test
attr test userReadings notifyReading:.* {\
# This codeblock calculates several other readings.\
# It triggers by any update of this device.\
\
#Either individually:\
#readingsBulkUpdate($hash, "uRA", perl_funktion_A());;\
#readingsBulkUpdate($hash, "uRB", perl_funktion_B());;\
#readingsBulkUpdate($hash, "uRC", perl_funktion_C());;\
\
#or your function does it in bulk:\
perl_funktion_die_alle_userReadings_fuer_den_Typ_A_zurueckgibt($hash);;\
\
#or your functions returns an array and we assign it from here to the differnent readings:\
# my @ret = perl_funktion_die_alle_userReadings_fuer_den_Typ_A_zurueckgibt();;\
# stub: foreach...\
\
#return some changing/different value to see we did process it:\
return sprintf("$reading executed at %02d:%02d:%02d", (localtime)[2,1,0]);;\
}
Bitte beachte, dass ich den $hash in deine Funktion übergeben würde, damit du dort die Funktion readingsBulkUpdate(...) ausführen kannst. Damit es schlank wird, kann man die Kommentare und Alternativen natürlich weglassen, das wollte ich dort nur im auch noch an den Stellen andeuten.

Ob das nun so eine übersichtliche Lösung wird, das darfst du dann selbst entscheiden. Ich finde ja, andere ggf. wieder nicht.

Beta-User

Zitat von: Torxgewinde am 08 Dezember 2023, 17:40:57Ob das nun so eine übersichtliche Lösung wird, das darfst du dann selbst entscheiden. Ich finde ja, andere ggf. wieder nicht.
Vielleicht ist es "übersichtlich", aber wenn man (wie der TE!) nicht die Untiefen des Systems kennt, sollte man die Finger von so einer Konstruktion lassen.

Just my2ct (mit einiger Erfahrung in der Betreuung von "kreativen" Neulingen).
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

frank

Zitat von: th0masrad am 08 Dezember 2023, 10:48:42Spontan denke ich, dass nun für jedes Device ein eigenes notify angelegt werden muss. Zwar wäre ein notify, das auf den Device-Typ reagiert, ideal, aber mMn. kann notify nur Gerätenamen als Suchmuster verarbeiten.

Wenn ich damit richtig liege, ist deine Lösung zwar gut, hätte aber den "Nachteil", für jedes Device noch ein notify einzubauen zu müssen...
ein notify kann durch events beliebig vieler devices getriggert werden, nicht nur eins!
genau das ist ein entscheidender vorteil eines notify gegenüber userreadings.
FHEM: 6.0(SVN) => Pi3(buster)
IO: CUL433|CUL868|HMLAN|HMUSB2|HMUART
CUL_HM: CC-TC|CC-VD|SEC-SD|SEC-SC|SEC-RHS|Sw1PBU-FM|Sw1-FM|Dim1TPBU-FM|Dim1T-FM|ES-PMSw1-Pl
IT: ITZ500|ITT1500|ITR1500|GRR3500
WebUI [HMdeviceTools.js (hm.js)]: https://forum.fhem.de/index.php/topic,106959.0.html

Beta-User

Zitat von: frank am 08 Dezember 2023, 18:11:11ein notify kann durch events beliebig vieler devices getriggert werden, nicht nur eins!
genau das ist ein entscheidender vorteil eines notify gegenüber userreadings.
Volle Zustimmung.

userReadings haben allerdings den "Vorteil", dass sie vom Ablauf her Teil der Readings-Aktualisierung sind, also "vor" dem Wirkungsbereich eines notify (etc!) liegen. Wenn (!!!!) man sowas also wirklich (!) braucht, ist es sinnvoll, userReadings zu verwenden. Allerdings sind das absolute Ausnahmen, und (soweit ich mich entsinne) sind es bei mir nur ZWave-Devices, bei denen da wirklich was mit userReadings los ist... (Und da werden die per attrTemplate gesetzt!)

Ich behaupte (bis ich den konkreten Anwndungsfall gesehen habe) weiter: der TE braucht eigentlich weder das notify noch irgendwelche userReadings....
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

th0masrad

#10
Danke, danke, danke! :-[

Ich weiß gar nicht, wo ich mit dem Antworten anfangen soll.

Zitat von: Beta-User am 08 Dezember 2023, 12:43:58Beim "Nicht-Vergessen" könnte eventuell das Modul "archetype" helfen
archetype kannte ich tatsächlich noch nicht. Es scheint aber ausschließlich für Attribute zuständig zu sein.
Edit: Und userReadings  sind Attribute. Somit können sie vom archetype verteilt werden. Habe ich erst auf den zweiten Blick begriffen. :-[

Zitat von: Beta-User am 08 Dezember 2023, 12:43:58Vielleicht magst du uns mit mehr Infos versorgen, welchem Zweck das dient?
Hier ein reales Beispiel, leicht gekürzt:
defmod HS_EG_KU_Fenster_links CUL_HM 1E909D
attr HS_EG_KU_Fenster_links DbLogInclude state,uRS,uRpSabotage,uRA,uRB,uRRFV,uRRFN
attr HS_EG_KU_Fenster_links alias Küchenfenster links
attr HS_EG_KU_Fenster_links devStateIcon {mU_devStateIcon_threeStateSensor($name, 'Fenster', 'mU_threeStateSensor_colorSchema_OpenIsBad')}
attr HS_EG_KU_Fenster_links group Küche
attr HS_EG_KU_Fenster_links icon TR_Fenster_Symbol
attr HS_EG_KU_Fenster_links model HM-SEC-RHS
attr HS_EG_KU_Fenster_links room Technik
attr HS_EG_KU_Fenster_links subType threeStateSensor
attr HS_EG_KU_Fenster_links userReadings uRstateIcon         {mU_getReading_state($NAME)},\
uRS                 {mU_shorten_sabotageError($NAME)},\
uRSIcon             {mU_shorten_sabotageError($NAME)},\
uRpriorSabotageIcon {mU_getReading_uRpriorSabotage($NAME)},\
uRcommStateIcon     {mU_getReading_commState($NAME)},\
uRA                 {mU_shorten_Activity($NAME)},\
uRAIcon             {mU_shorten_Activity($NAME)},\
uRB                 {mU_shorten_battery($NAME)},\
uRBIcon             {mU_shorten_battery($NAME)},\
uRRFV               {mU_determine_deviceAtFhem_rssi($hash)},\
uRRFN               {mU_determine_deviceAtFhem_name($hash)},
Erklärt natürlich immer noch nicht, warum die userReadings existieren. Es gibt sie, weil
  • einige Readings, die ich gerne hätte, nicht existieren,
  • manchmal die Einheiten (z.B. mA -> A) umgerechnet werden,
  • die originalen Namen und Werte so lang sind, dass sie die Datenbank unnötig aufblähen.
Das ganze ist über lange Zeit gewachsen. ;)

Zitat von: Beta-User am 08 Dezember 2023, 17:51:34Vielleicht ist es "übersichtlich", aber wenn man (wie der TE!) nicht die Untiefen des Systems kennt, sollte man die Finger von so einer Konstruktion lassen.
Ist relativ! Es müsste ein Forum "Fortgeschrittenenfragen" geben. 8)
Ich bin, wie da links unter meinem Usernamen steht, ein Intervall-FHEMler: Schon lange dabei und alle zwei bis drei Jahre bekomme ich einen Rappel, kaufe mit neue Hardware und stoße meine FHEM-Konfiguration um.

Zitat von: frank am 08 Dezember 2023, 18:11:11ein notify kann durch events beliebig vieler devices getriggert werden, nicht nur eins!
genau das ist ein entscheidender vorteil eines notify gegenüber userreadings.
Aber (:-[) ein notify benötigt den Gerätenamen. Es kann nicht auf einen Gerätetyp triggern. Ich müsste also meine Gerätenamen so ändern, dass sie den Typ im Namen enthalten.

Aktuell tendiere ich zu Torxgewinde! O:-)
Mit freundlichen Grüßen
Th0mas Rad.

frank

Zitat von: th0masrad am 08 Dezember 2023, 20:12:06Aber (:-[) ein notify benötigt den Gerätenamen.
warum immer singular? => "die namen"

ZitatIch müsste also meine Gerätenamen so ändern, dass sie den Typ im Namen enthalten.
nö.
FHEM: 6.0(SVN) => Pi3(buster)
IO: CUL433|CUL868|HMLAN|HMUSB2|HMUART
CUL_HM: CC-TC|CC-VD|SEC-SD|SEC-SC|SEC-RHS|Sw1PBU-FM|Sw1-FM|Dim1TPBU-FM|Dim1T-FM|ES-PMSw1-Pl
IT: ITZ500|ITT1500|ITR1500|GRR3500
WebUI [HMdeviceTools.js (hm.js)]: https://forum.fhem.de/index.php/topic,106959.0.html

th0masrad

#12
Zitat von: frank am 08 Dezember 2023, 21:57:20
ZitatIch müsste also meine Gerätenamen so ändern, dass sie den Typ im Namen enthalten.
nö.
Beinahe hätte ich dich schnippisch gebeten, mir zu zeigen, wie denn ein notify auf einen Device-Typ reagieren soll. Dann ist mir aber klar geworden, wie wir aneinander vorbei gedacht haben. ;D

Für mich war klar, dass ich alle Devices eines bestimmten Typs, z. B. einen CUL_HM threeStateSensor, mit den gleichen userReadings ausstatten werde. Daher mein Einwand, dass ein notify nicht auf den Device-Typ reagieren kann.

Du hast natürlich Recht, dass ein notify auf mehrere Gerätenamen reagieren kann. Ich müsste nur diese Gerätenamen (ggf. mit Regexp ;)) an das notify anfügen. Dann bestände das Inbetriebnehmen eines neuen Gerätes darin, seinen Namen, wenn er nicht schon durch die Regexp abgedeckt ist, an dieses notify anzufügen.

Und deshalb meinte ich, dass ich, um genau diese Regexp zu vereinfachen, den Gerätetyp mit in den Gerätenamen aufnehmen sollte!

Der Kreis hat sich geschlossen. ;D

Ich programmiere mal beide Varianten. Und setze den Thread auf gelöst.

Danke allerseits!
Mit freundlichen Grüßen
Th0mas Rad.