Ausreißer löschen/ Automatisch oder Skript

Fast durchgängig habe ich bei den Sensoren zur Messung des Stromverbrauchs Ausreißer, bei denen zwei bis dreistellige kWh “gemessen” werden. Gibt es nicht die Möglichkeit, mit einen Skript (oder einer Automation) alle Werte, die z. B. über 2kWh in 5 Minuten anzeigen, mit einer 0 zu überschreiben und wenn ja, wie sähe so ein Skript aus?

1 „Gefällt mir“

Ich wüsste nicht das es geht.
Du könntest aber mit dem Sensor-Wert in eine Filter-Funktion gehen und von dort aus eine neuen Sensor beschreiben

sensor:
  - platform: filter
    name: "sensor_gefiltert"
    entity_id: sensor._roh_wert
    filters:
      - filter: outlier
        window_size: 4
        radius: 2.0

Window_size: Betrachtet die letzten 4 Werte
Radius: Wert mehr als +/-2 vom Mittel abweicht dann wird verworfen

1 „Gefällt mir“

Danke, gute Idee. Das hieße, dass ich für mehr als 100 Sensoren dieser Art neue anlegen müsste. Das hilft mir dann leider nicht hinsichtlich der “historischen” Daten, richtig? Oder gibt es einen Weg, die alten Daten entsprechend zu filtern?

Aber es ist ein Ansatz, danke!

Ja die historischen Daten wären weg, sobald du die neuen „gefilterten Sensoren“ im Energy-Dashboard hinterlegst.

Ist die Ursache der Peaks bekannt?

Vielleicht kann man schon verhindern das die Peaks überhaupt zu HA gesendet werden?
Welche Geräte hast du da im Einsatz und wie wird an HA übertragen?

Nein, die Ursache der Peaks ist nicht bekannt und folgt keinem Schema. Auch sind Geräte unterschiedlicher Hersteller betroffen.

Werden denn die alten Daten ebenfalls korrigiert? Wenn ich das richtig sehe, zeichnet der neue Sensor dm doch erst ab dem Zeitpunkt auf, an dem er erstellt wurde.

Nein, der neue Sensor hat nichts mit den alten Daten zu tun und nimmt nur das ab dem Punkt der Erstellung.

Du sagtest, es sind viele Sensoren betroffen. Kann es etwas mit dem Netzwerk zu tun haben? Mir erscheint es so, als bekommt HA nicht alle Wert mit. Dann plötzlich doch wieder ein Wert Empfangen und HA summiert das einfach drauf…
Vielleicht dahingehend mal prüfen ob Werte nicht gesendet bzw. nicht ankommen und dann plötzlich doch wieder.

Nur eine Idee bevor du dir die Arbeit machst mit den ganzen Sensoren und bei filtern verfälscht du dir ja die Summe

Habe versucht das Problem mit einem Template wie folgt zu lösen:

* sensor:
  * name: “Qubino Rollo Fenster rechts Terasse Esszimmer Verbrauch \[kWh\] korrigiert”
    unit_of_measurement: kWh
    device_class: energy
    state_class: total_increasing
    state: >-
    {% set value = states(‘sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh’) | float(0) %}
    {% set const = 21474836.47 %}
    {% if value < 0 %}{{ (value + const) | round(2) }}
    {% else %}{{ value }}
    {% endif %}
    {% set value = states(‘sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh’) | float(0) %}
    {% set const = 0.2983333333 %}
    {% if value > 0.5 %}{{ (const) | round(2) }}
    {% endif %}

Offensichtlich scheint es hier ein Problem zu geben, da die hier errechneten Werte im Energie-Dashboard als “nicht verfügbar” erkannt werden. Wo ist der Fehler?

:crayon:by HarryP: Code-/Logzeilen formatiert (bitte immer in </> einbinden)
s.a.: (Neues Update & Features - Hier in der Community 🫶)

Es geht in die richtige Richtung mit ein wenig “aber”

  • 2 x {{ ... }} in einem Templatesensor? → Es kann nur einen geben (Rückgabewert)
  • states(‘sensor sowas ist sicher nur Kopierfehler → Hochkomma
  • Es gibt einen grauen Bereich, z.B. 0.4 → ist weder < 0 noch > 0.5
  • Eine unique_id würde ich auch immer mitgeben

Vorschlag:

sensor:
  - name: "Qubino Rollo Fenster rechts Terasse Esszimmer Verbrauch [kWh] korrigiert"
    unique_id: qubino_rollo_esszimmer_fenster_rechts_verbrauch_kwh_korrigiert
    unit_of_measurement: kWh
    device_class: energy
    state_class: total_increasing
    state: >-
      {% set value = states('sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh') | float(0) %}
      {% if value < 0 %}
        {% set const = 21474836.47 %}
        {{ (value + const) | round(2) }}
      {% elif value > 0.5 %}
        {% set const = 0.2983333333 %}
        {{ const | round(2) }}
      {% else %}
        {{ value | round(2) }}
      {% endif %}

Den 0.4 Fall fängt der Code aber auch nicht ab.
Was soll passieren bei Werten >=0 und <= 0.5?

EDIT:
Ich ziehe meinen Code Vorschlag zurück.
Je mehr ich darüber nachdenke, paßt das nicht zu einem total_increasing Sensor.
Ich bin aber jetzt zu müde, um tiefer zu gehen.

EDIT3:
Sorry nochmal, ich dachte gestern vor dem Schlafen gehen, komm die Syntaxfehler korrigierst Du noch schnell, merkte aber am Ende es liegt ein logisches Problem vor.

Neuer Ansatz

  • sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh → bleibt so mit Ausreißern
  • Sensor rollo_korrigiert → neuer Ansatz durch einen Trigger basierten Template anstatt fester Werte Korrektur und total_increasing
  • Dann erst ein Verbauchszähler, der rollo_korrigiert als Basis hat

Hier der Code zu rollo_korrigiert

template:
  - trigger:
      - platform: state
        entity_id: sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh
    sensor:
      - name: "Rollo korrigiert"
        unique_id: rollo_korrigiert
        unit_of_measurement: "kWh"
        device_class: energy
        state: >
          {% set new = states('sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh') | float(0) %}
          {% set old = states('sensor.rollo_korrigiert') | float(0) %}
          {# Neuer Wert wird ignoriert weil kann nicht kleiner sein als alter #}
          {% if new < old %}
            {{ old }}
          {# Wenn mehr als 0,5 kWh hinzugekommen -> wird als Ausreißer betrachtet und alter Wert genommen #}
          {% elif new - old > 0.5 %}
            {{ old }}
          {# Normale Werte #}
          {% else %}
            {{ new }}
          {% endif %}

Doku:

Tipp: Solche Sensoren treiben die Datenbankgröße. Sobald Du Vertrauen in die Mechanik bekommen hast, würde ich mindestens sensor.rollo_esszimmer_fenster_rechts_terasse_electric_consumption_kwh vom DB Recorder aussperren wenn nicht sogar auch rollo_korrigiert

Ich habe leider keine Lösung, aber exakt das gleiche Problem. Regelmäßig muss ich die Werte von Hand korrigieren und das nervt. Ein Muster kann ich auch nicht erkennen, wenn auch oft die gleichen Geräte betroffen sind. Ungünstigerweise hin und wieder auch Werte, die man nicht korrigieren kann, wie z.B. Temperaturwerte. Vor dem Filtern bin ich auch zurückgeschreckt, HA sollte sowas eigentlich von Haus aus können/erkennen

Werte zwischen 0 und 0,5 sollen unverändert übernommen werden!

Danke für Deinen Vorschlag, das probiere ich sofort aus!

Ich rate davon ab auch wenn es erstmal gut aussieht. Neuen Ansatz habe ich oben per EDIT. Gutes Gelingen

Hallo

wenn man die Anleitungen hier im Forum und anderswo sich anschaut, werden die Ausreißer immer mit dem Wert 0 überschrieben. In meinem Fall habe ich beschlossen, den maximal größten Wert, den ein Gerät in fünf Minuten verbrauchen kan (3,58kWh/12 =0,298 kWh) zu verwenden. Daher habe ich das Script wie folgt erweitert:

  - sensor:
      - name: "Zwischenstecker Büro Laserdrucker Verbrauch [kWh] korrigiert"
        unique_id: zwischenstecker_laserdrucker_buro_verbrauch_kwh_korrigiert
        unit_of_measurement: kWh
        device_class: energy
        state_class: total_increasing
        state: >-
          {% set value = states('sensor.zwischenstecker_buro_laserdrucker_electric_consumption_kwh') | float(0) %}
          {% if value < 0 %}
            {% set const = 21474836.47 %}
            {% if (value + const) > 0.5 %}
                {% set const = 0.2983333333 %}
                {{ const | round(2) }}
            {% else %}I
                {{ (value + const) | round(2) }}
            {% endif %}    
          {% elif value > 0.5 %}
            {% set const = 0.2983333333 %}
             {{ const | round(2) }}
          {% else %}I
            {{ value | round(2) }}
          {% endif %} 

Wie siehst Du das?

Ich halte das für konzeptionell problematisch.

Ausreißer bedeuten unglaubwürdige Messungen und das Ersetzen durch Konstanten ist inhaltlich fraglich und könnte gerade bei einem Verbrauchssensor zu Verzerrungen insbesondere im Energyboard führen (gibt es auch im Forum genug Hilferufe insbesondere wenn der gemessene Sensor kurz ausfällt). Der einzig saubere Weg ist, Ausreißer vor dem Verbrauchssensor zu ignorieren (herauszufiltern).

Du kannst doch beide Methoden wählen mit jeweils anderen Namen und schaust Dir das 1 Monat mal an und ziehst Deine eigenen Schlüsse.

Habe aufgrund diverser Posts mir zur Korrektur der Ausreißer folgendes SQL-Skript zusammen gebastelt:

WITH target_sensor AS (
    -- HIER die Entity-ID des Shellys eintragen
    SELECT id FROM statistics_meta WHERE statistic_id = 'sensor.zwischenstecker_waschkuche_trockner_electric_consumption_kwh'
),
error_marks AS (
    -- 1. Wir finden die "Phantom-Sprünge" (größer als 3.6 kWh)
    SELECT 
        s.id,
        s.start_ts,
        -- Wir definieren den gesamten Zuwachs dieser Stunde als Fehler
        (s.sum - s_prev.sum) as offset_delta
    FROM statistics s
    JOIN statistics s_prev ON s.metadata_id = s_prev.metadata_id 
         AND s_prev.start_ts = (SELECT MAX(start_ts) FROM statistics WHERE metadata_id = s.metadata_id AND start_ts < s.start_ts)
    WHERE s.metadata_id = (SELECT id FROM target_sensor)
      AND (((s.sum - s_prev.sum) > 3.6) OR ((s.sum - s_prev.sum)< 0))),
       -- Schwelle für den kWh-Fehler (>3,6 oder kleiner 0)
all_data AS (
    SELECT 
        s.id,
        s.start_ts,
        s.sum as original_sum,
        em.offset_delta
    FROM statistics s
    LEFT JOIN error_marks em ON s.id = em.id
    WHERE s.metadata_id = (SELECT id FROM target_sensor)
)
SELECT 
    datetime(start_ts, 'unixepoch', 'localtime') as zeitpunkt,
    ROUND(original_sum, 2) as summe_alt,
    ROUND(SUM(COALESCE(offset_delta, 0)) OVER (ORDER BY start_ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 2) as abgezogener_fehler,
    ROUND(original_sum - SUM(COALESCE(offset_delta, 0)) OVER (ORDER BY start_ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 2) as summe_neu_geglättet
FROM all_data
ORDER BY start_ts DESC;

Hat jemand eine Idee, wie ich die so errechneten neuen Werte auch tatsächlich durch Updates der jeweiligen Datensätze aktualisieren/ zurückschreiben kann?