[patch] 98_JsonMod.pm: Event, wenn keine readings erzeugt werden

Begonnen von betateilchen, 09 August 2023, 16:48:24

Vorheriges Thema - Nächstes Thema

betateilchen

Hallo Jörg,

ich schlage den untenstehenden patch zum Einbau in 98_JsonMod.pm vor.

Warum?
  • Ich frage Unwetterdaten von NINA per JsonMod ab.
  • Irgendwann gibt es keine Meldungen mehr, dann kommt ein leeres array zurück
  • JsonMod beendet stillschweigend die Verarbeitung, ohne die aus dem vorherigen Lauf erzeugten readings zu löschen
  • JsonMod erzeugt in diesem Fall überhaupt keinen Event, um auf "keine Meldungen vorhanden" reagieren zu können.

Da ich die Info über vorliegende Meldungen per mqtt direkt aus dem JsonMod-device innerhalb meiner Installation(en) und auf diverse Wanddisplays in der Wohnung verteile, wird dort also immer die letzte Meldung solange angezeigt, bis es wieder eine aktuelle Meldung gibt. Das ist sehr unschön und die Anzeige entspricht auch nicht den Tatsachen.

Der patch sorgt dafür, dass...

  • die vorhandenen readings gelöscht werden
  • ein event getriggert wird, auf den man beispielsweise per notify reagieren kann

Über eine wohlwollende Prüfung freue ich mich sehr.

Index: /opt/fhem/FHEM/98_JsonMod.pm
===================================================================
--- /opt/fhem/FHEM/98_JsonMod.pm        (revision 27834)
+++ /opt/fhem/FHEM/98_JsonMod.pm        (working copy)
@@ -53,6 +53,7 @@
                        update-on-start:0,1
                        readingList:textField-long
                        disable:0,1
+                       deleteIfNoReadings:0,1
                        interval
                );
        };
@@ -546,6 +547,13 @@
                };
                readingsBulkUpdate($hash, '.computedReadings', join ',', @newReadings);
                readingsEndUpdate($hash, 1);
+       } elsif (AttrVal($name,'deleteIfNoReadings',0)) {
+               my @oldReadings = split ',', ReadingsVal($name, '.computedReadings', '');
+               foreach my $k (keys %{$oldReadings}) {
+                       readingsDelete($hash, $k) if ($oldReadings->{$k} == 0 and any { $_ eq $k} @oldReadings);
+               };
+               readingsDelete($hash,'.computedReadings');
+               DoTrigger($name, '.computedReadingsNone');
        };
 
 };
@@ -1940,10 +1948,16 @@
        <ul>
                <a name="interval"></a>
                <li>interval<br>
-                       <code>set &lt;name&gt; interval &lt;*/15 * * * *&gt;</code><br>
+                       <code>attr &lt;name&gt; interval &lt;*/15 * * * *&gt;</code><br>
                        utilize a cron expression to define the interval at which the source file will be loaded.
                        Default is one hour.
                </li>
+               <a name="deleteIfNoReadings"></a>
+               <li>deleteIfNoReadings<br>
+                       <code>attr &lt;name&gt; deleteIfNoReadings &lt;0|1&gt;</code><br>
+                       delete all existing readings if payload contains no data.
+
+               </li>
                <a name="readingList"></a>
                <li>readingList<br>
                        Specifies the access to json elements and their representation as well as formatting as reading.

-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

herrmannj

den patch hatte ich nicht gesehen, das Anliegen ist nachvollziehbar.

Allerdings sollte das imho default sein. Wir sind in dynamischer Anzahl von readings: aus 10 eins wird werden 9 entfernt. Ergo sollte 10 entfernt werden wenn aus 10 0 werden ....

Ich habe testweise folgende logik eingebaut (aber nicht aktiviert):
attr onEmptyResponse (mit dem Namen bin ich noch nicht glücklich, die response muss ja nicht empty sein, lediglich der path...)
- wenn das auf "keep" steht werden die letzten readings erhalten und '.computedReadingsKeep' wird getriggered
- wenn das auf set <r> <v> steht, wird ein reading "r" mit "v" erzeugt. In deinem Fall möglicherweise "Meldung" "Keine"
- 0 oder nicht vorhanden (default): die readings werden gelöscht und ein event getriggert

Bin aber nicht zufrieden damit. Konsequent wäre es imho multi um ein default zu erweitern (analog zu single) und den pfad 3 oben (alles andere wird entfernt) zu implementieren. Ich denke noch über mögliche Nebenwirkungen nach.

In deinem Fall würde man dann zb multi() als param 3 und 4 "Meldung", "keine" mitgeben und wenn der path leer ist, wird das reading so erzeugt. Das würde auch funktionieren wenn single() und multi() gemischt verwendet werden.

betateilchen

Moin, danke fürs Kümmern :)

Das Attribut habe ich bei mir mal scharfgeschaltet und ich werde das mal testen.

Dass man dann ein reading erzeugen lassen kann, das mir meldet, dass es keine readings gibt, erinnert mich ein bisschen an meine Bundeswehrzeit vor fast 40 Jahren, als man auch melden musste, dass es nix zum Melden gibt - fand ich damals schon merkwürdig.

Wegen des Attributnamens: wenn Du mit onEmptyResponse unglücklich bist, wären vielleicht onEmptyJson oder onEmptyJsonPath schönere Alternativen?

Wegen des default-Verhaltens: ja, es hätte eigentlich von Anfang an default sein können. Aber jetzt nachträglich das default zu verändern, kann natürlich beim einen oder anderen Anwender zu Verwirrung führen.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

betateilchen

Hallo Jörg,

das Attribut habe ich erfolgreich getestet, es funktioniert, wie von Dir beschrieben und bewirkt genau das, was ich mir vorgestellt hatte.

mit Attributwert "keep":
10:03:14.762 RCVD fhemaws/nina/state .computedReadingsKeep

mit Attributwert "set readings none":
10:12:45.197 RCVD fhemaws/nina/readings none

ohne Attribut:
10:11:39.311 RCVD fhemaws/nina/state .computedReadingsNone

Dankeschön :)
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

herrmannj

Moin,

ja, das löst den einen use-case sicher zufriedenstellend. Aber: falls single() und multi() gemeinsam verwendet werden (oder mehrer multi()), dann bekommt man damit nicht dir versprochenen Ergebnisse.

Ich habe gerade eine Version eingecheckt welche das 'Problem' imho deutlich konsistenter löst:
- multi() hat 2 weitere (optionale) Parameter: defaultReading und defaultValue.
  - wenn angegeben und der JsonPath löst auf eine leere Menge (0 oder nicht vorhanden) auf, wird ein Reading daraus erzeugt.
  - wenn nicht angegeben werden evtl vorhandenen readings gelöscht, so wie das eigentlich 'hätte sein sollen'
- zusätzlich wird für jedes entfernte reading (egal warum) ein event ".readingsDelete $k" erzeugt, auf das man bei Bedarf reagiert.

Damit ist das Verhalten konsistent in Bezug auf single() und defaults und die ganzen Mischformen werden ebenfalls sauber behandelt.

(btw: ich habe einen ähnlichen Fall mal mit mehreren single() erschlagen. Der erste hatte default "keine Meldung", die anderen nicht. Führt, in Bezug auf die erzeugten readings, zum gleichen Ergebnis). So oder so, ich meine das ist deutlich runder.

Prüf mal bitte, imho erschlägt das deinen use-case komplett. GGf bitte auch mit corner-cases testen.

betateilchen

Da fehlt jetzt aber der zentrale event .computedReadingsNone der mich "einfach" darüber informiert, dass überhaupt keine readings erzeugt wurden.

Wenn im JsonMod device bereits vor dem reread keine readings vorhanden sind und im reread auch keine neuen readings gefunden werden, dann bekomme ich davon gar nichts mit, weil die readingsDelete events logischerweise nicht auftreten.

Das mit den events für readingsDelete ist zwar nett, aber nicht immer zielführend, weil ich nicht immer im Vorfeld weiß, welche readings es eventuell nicht mehr gibt und ich somit auch nicht festlegen kann, worauf ich triggern soll.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

herrmannj

wenn es nur darum geht; ist ergänzt! (computedReadingsNone wird getriggered)

ZitatWenn im JsonMod device bereits vor dem reread keine readings vorhanden sind und im reread auch keine neuen readings gefunden werden, dann bekomme ich davon gar nichts mit, weil die readingsDelete events logischerweise nicht auftreten.
Das ist unlogisch, wenn (speziell in deinem fall) vorher keine Meldung da war - muss auch keine auf dem Display gelöscht werden.
ZitatDas mit den events für readingsDelete ist zwar nett, aber nicht immer zielführend, weil ich nicht immer im Vorfeld weiß, welche readings es eventuell nicht mehr gibt und ich somit auch nicht festlegen kann, worauf ich triggern soll.
Doch, Du kennst und steuerst doch das Namensschema beim anlegen.

Nochmal, der Haken mit dem Angang von gestern ist: wenn jemand (nicht Betateilchen, bist nicht alleine auf der Welt) ein device anlegt wo single() und multi() gemischt verwendet werden, und das single() liefert (das multi() nicht) gibts da gar kein event.

Du kannst dir jetzt (auch) ein default für multi() hinterlegen und darauf triggern. Damit ist ganz klar, dieses eine multi() hat keinen Inhalt geliefert -> Meldungen löschen. Oder nimmst einen der andere 3 Wege (".computedReadingsNone", ".readingsDelete", single() mit default)

betateilchen

Zitat von: herrmannj am 04 September 2023, 15:30:36wenn es nur darum geht; ist ergänzt! (computedReadingsNone wird getriggered)

Danke!

ZitatDas ist unlogisch, wenn (speziell in deinem fall) vorher keine Meldung da war - muss auch keine auf dem Display gelöscht werden.

Dieser Gedankengang ist nachvollziehbar, aber ich verwende eingehende Trigger auch zur Überwachung, ob eine Anbindung überhaupt noch funktioniert. Ja, das kann man auch anders machen, aber ein event ist die einfachste Variante und quasi als Abfallprodukt.

Zitat von: herrmannj am 04 September 2023, 15:30:36Nochmal, der Haken mit dem Angang von gestern ist: wenn jemand (nicht Betateilchen, bist nicht alleine auf der Welt)

Offenbar scheint sich ja außer betateilchen niemand für das bisherige Verhalten interessiert, geschweige denn, sich daran gestört zu haben...
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!