Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Entwicklung
    4. SetState() nur bei Änderung von Werten?!?

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    SetState() nur bei Änderung von Werten?!?

    This topic has been deleted. Only users with topic management privileges can see it.
    • jens.maus
      jens.maus last edited by

      Hallo Zusammen,

      ich arbeite gerade an einem neuen Adapter der in regelmäßigen Abständen tausende Werte irgendwo abholt diese dann für ioBroker aufbereitet und nach anlegen der State die Werte dann entsprechend mittels setState(…., {ack=true, val=XXXX}); an ioBroker meldet. Das funktioniert soweit auch sehr gut.

      Nun stelle ich mir jedoch gerade die Frage ob es neben der setState() Funktion noch eine ähnliche Funktion gibt die den state allerdings nur dann auf einen neuen wert setzt wenn dieser nicht bereits diesen wert hat. Mir war nämlich aufgefallen das im ObjectView meiner State diese immer regelmäßig im Abholinterval meines Adapters grün auflashen was für bedeutet das die state auch bei gleichen werten immer neu gesetzt werden und das empfinde ich als unnötigen Overhead da ich ja wie gesagt mehrere tausend states bei jedem abholinterval (wohl jede 1-5 minuten) mittels setState() an ioBroker melde.

      Gibt es also irgendeine Möglichkeit/Funktion einen setState() nur dann auszuführen wenn der Wert des States nicht der selbe ist denn man gerade melden will? Oder muss ich das selbstständig hier mittels eines Konstruktes aus "if(getState() != val) setState()" ? Denn auch das erscheint mir unnötiger Overhead auf der Adapter-Seite zu sein, oder mache ich mir da unnötige Gedanken?

      1 Reply Last reply Reply Quote 0
      • M
        moebius last edited by

        tausende Werte? Klingt interessant - bei homematic knallen sicher schon die Sektkorken 🙂

        Verrätst du auch was das für Werte sind?

        Jedenfalls könntest du die Funktionalität im Adapter selber abbilden, falls in ioBroker die Werte dann nur mehr gelesen werden…

        1 Reply Last reply Reply Quote 0
        • Jeeper.at
          Jeeper.at last edited by

          Hallo,

          Ich würde diesbezüglich mal Apollon77 anschreiben. Er hat so etwas in des SQL Adapter eingebaut. Er kann dir sicher sagen wie er das gelöst hat.

          Lg

          Günther

          1 Reply Last reply Reply Quote 0
          • paul53
            paul53 last edited by

            @jens.maus:

            mittels eines Konstruktes aus "if(getState() != val) setState()" ? Denn auch das erscheint mir unnötiger Overhead auf der Adapter-Seite zu sein, `
            Den geringsten Overhead hat man sicherlich, wenn man für jeden Wert eine (globale) Skriptvariale hat, mit der verglichen wird, bevor sie aktualisiert und der Datenpunkt geändert wird.

            varN = getState(id).val;
            
            ...
            
            if(varN != val) {
              varN = val;
              setState(id, val, true);
            }
            
            1 Reply Last reply Reply Quote 0
            • apollon77
              apollon77 last edited by

              Einzige sinnvolle Variante ist die aktuellen Werte im Adapter-Prozess nochmal im Speicher zu haben. Dann kannst Du mit denen vergleichen und entscheiden wann Du neu in setState schreibst. Das kostet an sich nicht sooo viel Speicher.

              Am Ende ist halt die Frage was das für Werte sind und welche Dinge die Nutzer damit anstellen. Vllt ist es ja interessant auch "nicht geänderte Werte" getriggert zu bekommen. Das geht dann nicht mehr wenn Du Sie nur bei Änderungen schreibst. Aber bei "Pull-Szenarien" (also regelmäßiges abholen) ist das wohl eher nicht so relevant.

              1 Reply Last reply Reply Quote 0
              • jens.maus
                jens.maus last edited by

                @paul53:

                @jens.maus:

                mittels eines Konstruktes aus "if(getState() != val) setState()" ? Denn auch das erscheint mir unnötiger Overhead auf der Adapter-Seite zu sein, Den geringsten Overhead hat man sicherlich, wenn man für jeden Wert eine (globale) Skriptvariale hat, mit der verglichen wird, bevor sie aktualisiert und der Datenpunkt geändert wird.

                Oh, ich denke ich hab mich zu ungenau ausgedrückt. Es geht hier natürlich um die Entwicklung eines eigenen Adapters, dort gibt es nicht setState() sondern eben "adapter.setState()" genauso wie "adapter.getState()" und das verhält sich leider auch etwas anders, denn es ist immer asynchron sodass sowas z.B. nicht zu funktionieren scheint:

                for(var i=0; i < 100; i++) {
                  var newval = i;
                  var stateName = "test." + i;
                  adapter.getState(stateName, function(err, state) {
                    if(state.val != newval)
                      adapter.setState(stateName, {ack: true, val: newval);
                  });
                }
                
                

                Das Problem hierbei ist, wie gesagt, das adapter.getState() asynchron ist und somit "newval" und "stateName" innerhalb der anyonmen Funktion die an adapter.getState() übergeben wird zur Ausführungszeit leider die falschen werte hat. Zumindest ist das hier gerade der Fall.

                1 Reply Last reply Reply Quote 0
                • jens.maus
                  jens.maus last edited by

                  @apollon77:

                  Am Ende ist halt die Frage was das für Werte sind und welche Dinge die Nutzer damit anstellen. Vllt ist es ja interessant auch "nicht geänderte Werte" getriggert zu bekommen. Das geht dann nicht mehr wenn Du Sie nur bei Änderungen schreibst. Aber bei "Pull-Szenarien" (also regelmäßiges abholen) ist das wohl eher nicht so relevant. `

                  Ich denke auch das das nicht wirklich relevant ist für solche Pull-Szenarien. Hier würde es auch eher von Vorteil sein das nicht jedes adapter.setState() das an ioBroker abgesetzt wird darin endet dass dieser mit dem setzen der Werte und dem publisher an alle Interessenten beschäftigt ist. Das macht IMHO nur unnötigen Aufwand. Meiner Meinung nach wäre es sicherlich sinnvoll zusätzlich zu adapter.setState() eine Art adapter.setStateNotChanged() Funktion ode reinen zusätzlichen Parameter bei setState() einzuführen der erlaubt zu definieren ob ioBroker vor setzen des Wertes prüfen soll ob der Wert nicht ohnehin schon auf diesen Wert steht.

                  1 Reply Last reply Reply Quote 0
                  • Bluefox
                    Bluefox last edited by

                    @jens.maus:

                    @paul53:

                    @jens.maus:

                    mittels eines Konstruktes aus "if(getState() != val) setState()" ? Denn auch das erscheint mir unnötiger Overhead auf der Adapter-Seite zu sein, Den geringsten Overhead hat man sicherlich, wenn man für jeden Wert eine (globale) Skriptvariale hat, mit der verglichen wird, bevor sie aktualisiert und der Datenpunkt geändert wird.

                    Oh, ich denke ich hab mich zu ungenau ausgedrückt. Es geht hier natürlich um die Entwicklung eines eigenen Adapters, dort gibt es nicht setState() sondern eben "adapter.setState()" genauso wie "adapter.getState()" und das verhält sich leider auch etwas anders, denn es ist immer asynchron sodass sowas z.B. nicht zu funktionieren scheint:

                    for(var i=0; i < 100; i++) {
                      var newval = i;
                      var stateName = "test." + i;
                      adapter.getState(stateName, function(err, state) {
                        if(state.val != newval)
                          adapter.setState(stateName, {ack: true, val: newval);
                      });
                    }
                    
                    

                    Das Problem hierbei ist, wie gesagt, das adapter.getState() asynchron ist und somit "newval" und "stateName" innerhalb der anyonmen Funktion die an adapter.getState() übergeben wird zur Ausführungszeit leider die falschen werte hat. Zumindest ist das hier gerade der Fall. `
                    Das macht man einbisschen anders:

                    function setStates(_states, callback) {
                    	if (!_states || !_states.length) {
                    		if (typeof callback === 'function') callback();
                    		return;
                    	}
                    	var newState = _states.shift();
                    	adapter.getState(newState.name, function (err, oldState) {
                    		if (newState.val != oldState.val) {
                    			adapter.setState(newState.name, {ack: true, val: newState.val}, function () {
                    				setTimeout(setStates, 0, _states, callback);
                    			});
                    		} else {
                    			setTimeout(setStates, 0, _states, callback);
                    		}
                    	});
                    }
                    
                    var states = [];
                    for (var i = 0; i < 100; i++) {
                    	var newval = i;
                    	var stateName = 'test.' + i;
                    	states.push({name: stateName, val: newval};
                    }
                    
                    setStates(states, function () {
                    	console.log('finished');
                    });
                    
                    

                    Falls die Werte nicht zu gross sind (ein Wert unter 1kb) ich empfehle dir alle 1000 Werte in RAM im Adapter zu halten.

                    1 Reply Last reply Reply Quote 0
                    • apollon77
                      apollon77 last edited by

                      @jens.maus:

                      Meiner Meinung nach wäre es sicherlich sinnvoll zusätzlich zu adapter.setState() eine Art adapter.setStateNotChanged() Funktion ode reinen zusätzlichen Parameter bei setState() einzuführen der erlaubt zu definieren ob ioBroker vor setzen des Wertes prüfen soll ob der Wert nicht ohnehin schon auf diesen Wert steht. `

                      Meines WIssens macht die "setState" Logik das schon. Wenn der Wert nicht geändert ist wird der "lc" (=lastchanged") Timestamp nicht geändert sondern nur "ts" (Timestamp"). Also die Erkennungslogik ist da schon drin.

                      Und bei den Triggern kann man "ne" als "not equal" angeben, sodass man nur bei Änderungen notifiziert wird …

                      1 Reply Last reply Reply Quote 0
                      • jens.maus
                        jens.maus last edited by

                        @apollon77:

                        Meines WIssens macht die "setState" Logik das schon. Wenn der Wert nicht geändert ist wird der "lc" (=lastchanged") Timestamp nicht geändert sondern nur "ts" (Timestamp"). Also die Erkennungslogik ist da schon drin.

                        Und bei den Triggern kann man "ne" als "not equal" angeben, sodass man nur bei Änderungen notifiziert wird … `

                        Nun, dann ist aber doch immer noch der Overhead da das der normale "ts"-Timestamp angepasst werden muss von ioBroker. Des Weiteren sehe ich zumindest im admin adapter das alle state zur selben zeit immer grün aufleuchten, d.h. also eine änderung/anpassung anzeigen immer genau dann wenn mein adapter wieder Intervall-basiert die Werte mit setState() einfach blind setzt.

                        1 Reply Last reply Reply Quote 0
                        • apollon77
                          apollon77 last edited by

                          Korrekt. Auch wenn sich nur "ts" ändern ist das eine Änderung und wird an alle Subscriber notifiziert die das wissen wollen … Und ich denke Admin will alles wissen 🙂

                          So oder so: Ja bei Pull-Szenarien macht es Sinn nicht jede "Nicht-Änderung" per setState zu senden sondern nur Änderungen.

                          Also lokal die Werte Speichern (geht übrigens auch für mehr als 1000 Datensätze noch ... wird halt irgendwann zur Speicherverbrauchsfrage.

                          Ingo F

                          1 Reply Last reply Reply Quote 0
                          • jens.maus
                            jens.maus last edited by

                            @apollon77:

                            Korrekt. Auch wenn sich nur "ts" ändern ist das eine Änderung und wird an alle Subscriber notifiziert die das wissen wollen … Und ich denke Admin will alles wissen 🙂 `

                            Das stimmt wohl, aber IMHO sollte Admin zumindest keine Änderung der States anzeigen (mit grünem Aufflackern) wenn sich nichts am Wert geändert hat. Vielleicht könnte man das ja im admin adapter mal anpassen/ändern?!?

                            Auch wäre es sicherlich sinnvoll wenn man dem Subscribe Befehl eine Option/Möglichkeit hinzufügen könnte nur auf Wertänderungen zu reagieren und nicht auf allgemeine Änderungen (z.b. anpassen der 'ts'), dann würde sich auch hier der overhead für alle subscriber sicherlich weiter reduzieren denn mit jedem weiteren Adapter wird das system ja dann nicht unnötig jeden adapter bei jeder Anpassung an einen state benachrichtigen sondern nur eben die die wirklich auch auf reine ts Änderungen reagieren wollen (was sicherlich die wenigstens wirklich wollen).

                            @apollon77:

                            So oder so: Ja bei Pull-Szenarien macht es Sinn nicht jede "Nicht-Änderung" per setState zu senden sondern nur Änderungen. `

                            Genau deshalb argumentiere ich ja für diese Szenarien den adapter.setState() befehl dahingehend zu erweitern das man ihm mitteilen kann das er nur den State anpassen soll wenn der Wert des State wirklich unterschiedlich ist. Das könnte man ja IMHO ähnlich wie mit dem "ack=true/false" machen in dem man hier sowas wie folgendes implementiert:

                            adapter.setState(state, {val: 1, ack=true, forceChange=false});
                            
                            

                            In dem falle würde dann bei "forceChange=false" der setState Befehl eben selbst überprüfen ob sich "val" wirklich ändert und dann eben nur im falle einer Änderung wirklich weitermachen. Und forceChange würde natürlich per default auf true stehen zwecks rückwärts-kompatibilität. Denn wenn diese Möglichkeit geschaffen würde, dann müssten eben genau die Adapter die reine Pull-Szenarien bedienen nicht immer selbst solche recht komplexen Dinge wie Bluefox sie gezeigt hat selbst implementieren.

                            Denkst du das macht Sinn? Wenn ja könnte ich mir das mal anschauen und ggf. euch einen PullRequest dazu schicken wenn ich die Stelle finde und entsprechend angepasst bekomme?!?!

                            @apollon77:

                            Also lokal die Werte Speichern (geht übrigens auch für mehr als 1000 Datensätze noch … wird halt irgendwann zur Speicherverbrauchsfrage. `

                            Naja, ich muss ja nicht wirklich mir die Daten merken sondern kann Sie eben per getState() wie von Bluefox gezeigt vorher abholen und selbst vergleichen, zumindest hab ich das hier so vor zu tun. Chicer wäre natürlich eben besagte "forceChange=false" Anpassungen an setState() 🙂

                            1 Reply Last reply Reply Quote 0
                            • apollon77
                              apollon77 last edited by

                              @jens.maus:

                              Auch wäre es sicherlich sinnvoll wenn man dem Subscribe Befehl eine Option/Möglichkeit hinzufügen könnte nur auf Wertänderungen zu reagieren und nicht auf allgemeine Änderungen (z.b. anpassen der 'ts') `

                              Der JavaScript-Adapter macht das schon so: https://github.com/ioBroker/ioBroker.ja ... some-state

                              Zu der setState-Idee müsste Bluefox was sagen 🙂

                              Ingo F

                              1 Reply Last reply Reply Quote 0
                              • jens.maus
                                jens.maus last edited by

                                @apollon77:

                                @jens.maus:

                                Auch wäre es sicherlich sinnvoll wenn man dem Subscribe Befehl eine Option/Möglichkeit hinzufügen könnte nur auf Wertänderungen zu reagieren und nicht auf allgemeine Änderungen (z.b. anpassen der 'ts') `

                                Der JavaScript-Adapter macht das schon so: https://github.com/ioBroker/ioBroker.ja ... some-state `

                                Na dann sollte doch IMHO der adapter.subscribeStates() Methode die gleiche Funktionalität spendiert werden 😉

                                @apollon77:

                                Zu der setState-Idee müsste Bluefox was sagen 🙂 `

                                Na dann hoffe ich mal er liest hier aufmerksam mit. 🙂 Gerne kann ich auch (um die Verständigung zu verbessern) das ganze nochmal in Englisch verfassen wenn das helfen sollte.

                                1 Reply Last reply Reply Quote 0
                                • apollon77
                                  apollon77 last edited by

                                  Denke Deutsch passt 🙂

                                  1 Reply Last reply Reply Quote 0
                                  • jens.maus
                                    jens.maus last edited by

                                    Hier noch ein Argument dafür eine setState() option zu schaffen bei der nur bei Änderungen des val wertes wirklich das setState durchkommt:

                                    2017-01-20 14:01:49.187  - info: admin.0 Unsubscribe from all states, except system's, because over 3 seconds the number of events is over 60 (in last second 0)
                                    2017-01-20 14:02:20.636  - info: admin.0 Subscribe on all states again
                                    
                                    

                                    Dies passiert bei mir jetzt bei jedem Abholen der Datenwerte (jede X Minuten). Hab zwar nun die Methode die Bluefox hier beschrieben hat implementiert und das klappt auch gut, trotzdem denke ich eine Erweiterung der setState() method könnte nicht schaden.

                                    1 Reply Last reply Reply Quote 0
                                    • AlCalzone
                                      AlCalzone Developer last edited by

                                      @jens.maus:

                                      Es geht hier natürlich um die Entwicklung eines eigenen Adapters, dort gibt es nicht setState() sondern eben "adapter.setState()" genauso wie "adapter.getState()" und das verhält sich leider auch etwas anders, denn es ist immer asynchron sodass sowas z.B. nicht zu funktionieren scheint:

                                      for(var i=0; i < 100; i++) {
                                        var newval = i;
                                        var stateName = "test." + i;
                                        adapter.getState(stateName, function(err, state) {
                                          if(state.val != newval)
                                            adapter.setState(stateName, {ack: true, val: newval);
                                        });
                                      }
                                      
                                      

                                      Das Problem hierbei ist, wie gesagt, das adapter.getState() asynchron ist und somit "newval" und "stateName" innerhalb der anyonmen Funktion die an adapter.getState() übergeben wird zur Ausführungszeit leider die falschen werte hat. Zumindest ist das hier gerade der Fall. `

                                      Bin ein bisschen spät mit der Antwort, aber vielleicht hilft es ja noch zum Verständnis. Bluefox hat dir eine recht elegante Version geschrieben, die sequentiell abläuft. Man kann das Problem aber auch lösen, indem man innerhalb der Schleife eine Funktion definiert, die die Schleifenvariable kapselt -> Stichwort Closure.

                                      for(var i=0; i < 100; i++) {
                                        // Hier beginnt die umhüllende Funktion
                                        (function(i) {
                                          // Der Wert von i ändert sich innerhalb dieser Funktion nicht mehr, er ist gekapselt
                                          var newval = i;
                                          var stateName = "test." + i;
                                          adapter.getState(stateName, function(err, state) {
                                            if(state.val != newval)
                                              adapter.setState(stateName, {ack: true, val: newval);
                                          });
                                        // und hier wird die Funktion aufgerufen und der aktuelle Wert der Schleifenvariable übergeben
                                        })(i);
                                      }
                                      
                                      

                                      Der Code ist ungetestet, sollte aber laufen. Ich hoffe, ich habe mich jetzt nicht vertippt.

                                      1 Reply Last reply Reply Quote 0
                                      • First post
                                        Last post

                                      Support us

                                      ioBroker
                                      Community Adapters
                                      Donate

                                      798
                                      Online

                                      31.7k
                                      Users

                                      79.8k
                                      Topics

                                      1.3m
                                      Posts

                                      7
                                      17
                                      4165
                                      Loading More Posts
                                      • Oldest to Newest
                                      • Newest to Oldest
                                      • Most Votes
                                      Reply
                                      • Reply as topic
                                      Log in to reply
                                      Community
                                      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                                      The ioBroker Community 2014-2023
                                      logo