Strombörse

Aus FHEMWiki
Zur Navigation springen Zur Suche springen
EVU_Tibber_connect

Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.

Als Beispiel wären da:

  1. Tibber - im Forum
  2. aWATTar - im Forum

Diese Wiki Seite ist noch in Bearbeitung!!!

1. Tibber mit Tibber Pulse

Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als "Tibber Pulse" vertrieben wird. Damit wird ein bestehender digitaler EVU Zähler ausgelesen und die Werte per LAN zu Tibber gesendet. Über eine API bei Tibber können dann die Verbrauchswerte und Kosten, sowie ein Live Stream (WebSocket) abgefragt werden.

Die im Folgenden beschriebenen Beispieldevices bieten die oben genannte Abfrage der API mit WebSocket und HTTPMOD. Das EVU_Tibber Device dient der Darstellung und Steuerung.

Vorausetzungen

  • FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)
  • KeyValue() (wird hier beschrieben)
  • Tibber Pulse Lesekopf auf dem digitalen EVU Zähler
  • Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können

EVU_Tibber_connect

Das EVU_Tibber_connect fragt mit HTTPMOD Statistiken auf der Tibber API Webseite ab. Zusätzlich wurde in den userReadings die WebSocket Verbindung für die Live Daten integriert.

  • Die WebSocket Verbindung wurde leicht angepasst von hier übernommen
  • Im Forum wurde dies ab hier bearbeitet

Verwendete Module

  • HTTPMOD
  • DbLog
  • DbRep
  • DOIF
  • KeyValue()

Ablage der homeID und des Tokens

Eine Verbindung zur Tibber API mit den tatsächlichen Preisen bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.

In diesem Beispiel Device kann man seine homeID und sein Token entweder als Attribut ablegen oder man macht einen Eintrag im KeyStore von FHEM, wodurch man nicht so leicht versehentlich seine Daten im Forum postet.

Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind. Die Funktion befindet sich in der 99_myUtils.pm .

Beispiel KeyValue()

Achtung, der KeyValue ist kein Schutz für Passwort oder Keys! Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind. Die Funktion befindet sich in der 99_myUtils .

KeyValue() Funktion in der ~/FHEM/99_myUtils.pm
sub KeyValue {
    my ($step, $index, $value) = @_;
    my $key = getUniqueId().$index;
    my $e_value = "";
    my $error;

    if (eval "use Digest::MD5;1") {
      $key    = Digest::MD5::md5_hex(unpack "H*", $key);
      $key   .= Digest::MD5::md5_hex($key);
    }
   
    if ($step eq "read") {
      ($error, $value) = getKeyValue($index);

      if ( defined($error) ) {
        Log3 $index,3, "$index, can't read key from FhemUtils/uniqueID: $error";
        return undef;
      }

      if ( defined($value) ) {
        my $dec_value = '';

        for my $char (map { pack('C', hex($_)) } ($value =~ /(..)/g)) {
          my $decode  = chop($key);
          $dec_value .= chr(ord($char)^ord($decode));
          $key        = $decode.$key;
        }
        return $dec_value;
      }
      else {
        Log3 $index,3,"$index, no key found in FhemUtils/uniqueID";
        return undef;
      }
    }

    if ($step eq "store") {
      for my $char (split //, $value) {
        my $encode = chop($key);
        $e_value  .= sprintf("%.2x",ord($char)^ord($encode));
        $key       = $encode.$key;
      }
      $error = setKeyValue($index, $e_value);
      return "error while saving key : $error" if(defined($error));
      return "Key successfully saved in FhemUtils/uniqueID Key $index";
    }

};
Ablegen der homeID und des Token

In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber. Das "read" ist dann auch direkt der erste Test, bevor man weiter macht.

{KeyValue("store","EVU_Tibber_connect_token","5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE")}
{KeyValue("store","EVU_Tibber_connect_homeID","96a14971-525a-4420-aae9-e5aedaa129ff")}

{KeyValue("read","EVU_Tibber_connect_token")}
{KeyValue("read","EVU_Tibber_connect_homeID")}
Beispiel Attribut für homeID und Token

Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.

attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          <<<< Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE

EVU_Tibber_connect RAW Device

Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrege der Live Daten innerhalb der userReadings eine WebSocket Verbindung zu Tibber aufgebaut. Tibber stellt jeweils gegen 14:00 Uhr die Daten für den nächsten Tag bereit, wodurch von 00:00 - 14:00 Uhr diese Information im EVU_Tibber_connect noch nicht angezeigt werden kann. Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. Die Verwendung von FileLog ist nicht unterstützt!

defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL
attr EVU_Tibber_connect DbLogExclude .*
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \
https://developer.tibber.com/explorer\
\
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\
\
{KeyValue("store","EVU_Tibber_connect_token","5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE")}\
{KeyValue("store","EVU_Tibber_connect_homeID","96a14971-525a-4420-aae9-e5aedaa129ff")}\
\
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\
setreading EVU_Tibber compensation_grid <Eure Einspeisevergütung>\
\
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\
    (fc_avg - fc_min)  /2 + fc_min\
\
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\
    (fc_avg - compensation_grid) *0.85
attr EVU_Tibber_connect disable 0
attr EVU_Tibber_connect enableControlSet 1
attr EVU_Tibber_connect get01-1Name current_currency
attr EVU_Tibber_connect get01-2Name current_level
attr EVU_Tibber_connect get01-3Name current_date
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get01-4Name current_price
attr EVU_Tibber_connect get01-4OExpr $val *100
attr EVU_Tibber_connect get01Data { "query": "{viewer {home(id:\"%%homeID%%\") {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}" }
attr EVU_Tibber_connect get01Header01 Content-Type: application/json
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current
attr EVU_Tibber_connect get01Name 01_priceInfo
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get02-10Name fc0_03_total
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-12Name fc0_04_total
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-14Name fc0_05_total
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-16Name fc0_06_total
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-18Name fc0_07_total
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-1Name current_date
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-20Name fc0_08_total
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-22Name fc0_09_total
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-24Name fc0_10_total
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-26Name fc0_11_total
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-28Name fc0_12_total
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-2Name current_price
attr EVU_Tibber_connect get02-2OExpr $val *100
attr EVU_Tibber_connect get02-30Name fc0_13_total
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-32Name fc0_14_total
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-34Name fc0_15_total
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-36Name fc0_16_total
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-38Name fc0_17_total
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-40Name fc0_18_total
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-42Name fc0_19_total
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-44Name fc0_20_total
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-46Name fc0_21_total
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-48Name fc0_22_total
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-4Name fc0_00_total
attr EVU_Tibber_connect get02-50Name fc0_23_total
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-52Name fc1_00_total
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-54Name fc1_01_total
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-56Name fc1_02_total
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-58Name fc1_03_total
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-60Name fc1_04_total
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-62Name fc1_05_total
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-64Name fc1_06_total
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-66Name fc1_07_total
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-68Name fc1_08_total
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-6Name fc0_01_total
attr EVU_Tibber_connect get02-70Name fc1_09_total
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-72Name fc1_10_total
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-74Name fc1_11_total
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-76Name fc1_12_total
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-78Name fc1_13_total
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-80Name fc1_14_total
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-82Name fc1_15_total
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-84Name fc1_16_total
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-86Name fc1_17_total
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-88Name fc1_18_total
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-8Name fc0_02_total
attr EVU_Tibber_connect get02-90Name fc1_19_total
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-92Name fc1_20_total
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-94Name fc1_21_total
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-96Name fc1_22_total
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02-98Name fc1_23_total
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get02Data { "query": "{viewer {home(id:\"%%homeID%%\") {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}" }
attr EVU_Tibber_connect get02Header01 Content-Type: application/json
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo
attr EVU_Tibber_connect get02Name 02_priceAll
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get03-1Name nodes_00_00_from
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost
attr EVU_Tibber_connect get03-2OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption
attr EVU_Tibber_connect get03-4Name nodes_00_01_from
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost
attr EVU_Tibber_connect get03-50OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost
attr EVU_Tibber_connect get03-5OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption
attr EVU_Tibber_connect get03-7Name nodes_00_02_from
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost
attr EVU_Tibber_connect get03-8OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption
attr EVU_Tibber_connect get03Data { "query": "{viewer {home(id:\"%%homeID%%\") {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}"}
attr EVU_Tibber_connect get03Header01 Content-Type: application/json
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get03Name 03_consumption_hour
attr EVU_Tibber_connect get03RegOpt g
attr EVU_Tibber_connect get03Regex \{"from":"([\d+-]+T[\d+:]+\.000[+-][\d+:]+)","cost":(null|\d+\.\d+|0),"consumption":(null|\d+\.\d+|0)\}
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get04-10Name nodes_24_03_from
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost
attr EVU_Tibber_connect get04-11OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption
attr EVU_Tibber_connect get04-13Name nodes_24_04_from
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost
attr EVU_Tibber_connect get04-14OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption
attr EVU_Tibber_connect get04-16Name nodes_24_05_from
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost
attr EVU_Tibber_connect get04-17OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption
attr EVU_Tibber_connect get04-19Name nodes_24_06_from
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-1Name nodes_24_00_from
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost
attr EVU_Tibber_connect get04-20OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption
attr EVU_Tibber_connect get04-22Name nodes_24_07_from
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost
attr EVU_Tibber_connect get04-23OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption
attr EVU_Tibber_connect get04-25Name nodes_24_08_from
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost
attr EVU_Tibber_connect get04-26OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption
attr EVU_Tibber_connect get04-28Name nodes_24_09_from
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost
attr EVU_Tibber_connect get04-29OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost
attr EVU_Tibber_connect get04-2OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption
attr EVU_Tibber_connect get04-31Name nodes_24_10_from
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost
attr EVU_Tibber_connect get04-32OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption
attr EVU_Tibber_connect get04-34Name nodes_24_11_from
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost
attr EVU_Tibber_connect get04-35OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption
attr EVU_Tibber_connect get04-37Name nodes_24_12_from
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost
attr EVU_Tibber_connect get04-38OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption
attr EVU_Tibber_connect get04-40Name nodes_24_13_from
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost
attr EVU_Tibber_connect get04-41OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption
attr EVU_Tibber_connect get04-43Name nodes_24_14_from
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost
attr EVU_Tibber_connect get04-44OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption
attr EVU_Tibber_connect get04-46Name nodes_24_15_from
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost
attr EVU_Tibber_connect get04-47OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption
attr EVU_Tibber_connect get04-49Name nodes_24_16_from
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-4Name nodes_24_01_from
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost
attr EVU_Tibber_connect get04-50OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption
attr EVU_Tibber_connect get04-52Name nodes_24_17_from
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost
attr EVU_Tibber_connect get04-53OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption
attr EVU_Tibber_connect get04-55Name nodes_24_18_from
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost
attr EVU_Tibber_connect get04-56OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption
attr EVU_Tibber_connect get04-58Name nodes_24_19_from
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost
attr EVU_Tibber_connect get04-59OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost
attr EVU_Tibber_connect get04-5OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption
attr EVU_Tibber_connect get04-61Name nodes_24_20_from
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost
attr EVU_Tibber_connect get04-62OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption
attr EVU_Tibber_connect get04-64Name nodes_24_21_from
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost
attr EVU_Tibber_connect get04-65OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption
attr EVU_Tibber_connect get04-67Name nodes_24_22_from
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost
attr EVU_Tibber_connect get04-68OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption
attr EVU_Tibber_connect get04-70Name nodes_24_23_from
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost
attr EVU_Tibber_connect get04-71OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption
attr EVU_Tibber_connect get04-7Name nodes_24_02_from
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost
attr EVU_Tibber_connect get04-8OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption
attr EVU_Tibber_connect get04Data { "query": "{viewer {home(id:\"%%homeID%%\") {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}"}
attr EVU_Tibber_connect get04Header01 Content-Type: application/json
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get04Name 04_consumption_hour_24
attr EVU_Tibber_connect get04RegOpt g
attr EVU_Tibber_connect get04Regex \{"from":"([\d+-]+T[\d+:]+\.000[+-][\d+:]+)","cost":(null|\d+\.\d+|0),"consumption":(null|\d+\.\d+|0)\}
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get05AutoNumLen 4
attr EVU_Tibber_connect get05Data { "query": "{viewer {home(id:\"%%homeID%%\") {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}"}
attr EVU_Tibber_connect get05Header01 Content-Type: application/json
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get05JSON data_viewer_home
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort
attr EVU_Tibber_connect get06-3Name Adresse_06_Land
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort
attr EVU_Tibber_connect get06-5Name Adresse_06_Land
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail
attr EVU_Tibber_connect get06Data { "query": "{viewer {home(id:\"%%homeID%%\") {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}" }
attr EVU_Tibber_connect get06Header01 Content-Type: application/json
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get06JSON data_viewer_home
attr EVU_Tibber_connect get06Name 06_address
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect get07-10Name nodes_24_03_from
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost
attr EVU_Tibber_connect get07-11OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption
attr EVU_Tibber_connect get07-13Name nodes_24_04_from
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost
attr EVU_Tibber_connect get07-14OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption
attr EVU_Tibber_connect get07-16Name nodes_24_05_from
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost
attr EVU_Tibber_connect get07-17OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption
attr EVU_Tibber_connect get07-19Name nodes_24_06_from
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost
attr EVU_Tibber_connect get07-20OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption
attr EVU_Tibber_connect get07-22Name nodes_24_07_from
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost
attr EVU_Tibber_connect get07-23OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption
attr EVU_Tibber_connect get07-25Name nodes_24_08_from
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost
attr EVU_Tibber_connect get07-26OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption
attr EVU_Tibber_connect get07-28Name nodes_24_09_from
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost
attr EVU_Tibber_connect get07-29OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-2Name features_id
attr EVU_Tibber_connect get07-2OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption
attr EVU_Tibber_connect get07-31Name nodes_24_10_from
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost
attr EVU_Tibber_connect get07-32OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption
attr EVU_Tibber_connect get07-34Name nodes_24_11_from
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost
attr EVU_Tibber_connect get07-35OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption
attr EVU_Tibber_connect get07-37Name nodes_24_12_from
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost
attr EVU_Tibber_connect get07-38OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption
attr EVU_Tibber_connect get07-40Name nodes_24_13_from
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost
attr EVU_Tibber_connect get07-41OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption
attr EVU_Tibber_connect get07-43Name nodes_24_14_from
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost
attr EVU_Tibber_connect get07-44OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption
attr EVU_Tibber_connect get07-46Name nodes_24_15_from
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost
attr EVU_Tibber_connect get07-47OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption
attr EVU_Tibber_connect get07-49Name nodes_24_16_from
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-4Name nodes_24_01_from
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost
attr EVU_Tibber_connect get07-50OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption
attr EVU_Tibber_connect get07-52Name nodes_24_17_from
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost
attr EVU_Tibber_connect get07-53OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption
attr EVU_Tibber_connect get07-55Name nodes_24_18_from
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost
attr EVU_Tibber_connect get07-56OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption
attr EVU_Tibber_connect get07-58Name nodes_24_19_from
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost
attr EVU_Tibber_connect get07-59OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost
attr EVU_Tibber_connect get07-5OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption
attr EVU_Tibber_connect get07-61Name nodes_24_20_from
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost
attr EVU_Tibber_connect get07-62OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption
attr EVU_Tibber_connect get07-64Name nodes_24_21_from
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost
attr EVU_Tibber_connect get07-65OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption
attr EVU_Tibber_connect get07-67Name nodes_24_22_from
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost
attr EVU_Tibber_connect get07-68OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption
attr EVU_Tibber_connect get07-70Name nodes_24_23_from
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost
attr EVU_Tibber_connect get07-71OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption
attr EVU_Tibber_connect get07-7Name nodes_24_02_from
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost
attr EVU_Tibber_connect get07-8OExpr ($val ne "0" and $val ne "null")? round($val,4) : $val
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption
attr EVU_Tibber_connect get07Data { "query": "{viewer {home(id:\"%%homeID%%\") {id features{realTimeConsumptionEnabled} } } }" }
attr EVU_Tibber_connect get07Header01 Content-Type: application/json
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%
attr EVU_Tibber_connect get07JSON data_viewer_home
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled
attr EVU_Tibber_connect get07RegOpt g
attr EVU_Tibber_connect get07Regex \{"from":"([\d+-]+T[\d+:]+\.000[+-][\d+:]+)","cost":(null|\d+\.\d+|0),"consumption":(null|\d+\.\d+|0)\}
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql
attr EVU_Tibber_connect group PV Steuerung EVU
attr EVU_Tibber_connect icon stromzaehler_icon
attr EVU_Tibber_connect replacement01Mode expression
attr EVU_Tibber_connect replacement01Regex %%token%%
attr EVU_Tibber_connect replacement01Value {KeyValue("read","EVU_Tibber_connect_token")}
attr EVU_Tibber_connect replacement02Mode expression
attr EVU_Tibber_connect replacement02Regex %%homeID%%
attr EVU_Tibber_connect replacement02Value {KeyValue("read","EVU_Tibber_connect_homeID")}
attr EVU_Tibber_connect requestData { "query": "{viewer {home(id:\"%%homeID%%\") {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}" }
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%
attr EVU_Tibber_connect room Strom->Boerse
attr EVU_Tibber_connect showBody 1
attr EVU_Tibber_connect showError 1
attr EVU_Tibber_connect sortby 313
attr EVU_Tibber_connect timeout 30
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\
    my $hash = $defs{$name};;\
    my $devState = DevIo_IsOpen($hash);;\
    return "Device already open" if (defined($devState));;\
    \
    # establish connection to websocket\
    # format must also include portnumber if a path is to be specified\
    $hash->{DeviceName} = AttrVal($name, "ws_websocketURL", "wss:echo.websocket.org:443");;\
    \
    # special headers needed for Tibber, see also Developer Tools in Browser\
    $hash->{header}{'Sec-WebSocket-Protocol'} = 'graphql-transport-ws';;\
    $hash->{header}{'Host'} = 'websocket-api.tibber.com';;\
    $hash->{header}{'Origin'} = 'https://developer.tibber.com';;\
    \
    # callback function when "select()" signals data for us\
    # websocket Ping/Pongs are treated in DevIo but still call this function\
    $hash->{directReadFn} = sub () {\
        my $hash = $defs{$name};;\
        \
        # we can read without closing the DevIo, because select() signalled data\
        my $buf = DevIo_SimpleRead($hash);;\
        \
        # if read fails, close device\
        if(!defined($buf)) {\
            DevIo_CloseDev($hash);;\
            $buf = "not_connected";;\
        }\
        \
        #Log(3, "$name:$reading: websocket data: >>>$buf<<<");;\
        \
        # only update our reading if buffer is not empty and if last update is older than minInterval\
        if ($buf ne "") {\
            my $websocketDataAge = ReadingsAge($name, "ws_websocketData", 3600);;\
            my $minInterval = AttrVal($name, "ws_minInterval", 0);;\
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\
            \
            readingsBeginUpdate($hash);;\
            readingsBulkUpdate($hash, "ws_websocketData", "$buf") if ($isNext && $websocketDataAge > $minInterval);;\
            readingsBulkUpdate($hash, "ws_websocketData", "$buf") if (!$isNext);;\
            readingsEndUpdate($hash, 1);;\
            #Log(3, "$name:$reading: websocket data written to reading");;\
        }\
    };;\
    \
    # open DevIo websocket\
    DevIo_OpenDev($hash, 0, undef, sub(){\
        my ($hash, $error) = @_;;\
        return "$error" if ($error);;\
        \
        my $token = AttrVal($name, "ws_token", "???");;\
           $token = KeyValue("read","EVU_Tibber_connect_token") if ($token eq "???");;\
\
        DevIo_SimpleWrite($hash, '{"type":"connection_init","payload":{"token":"'.$token.'"}}', 2);;\
    });;\
    readingsBulkUpdate($hash, "ws_websocketData", "");;\
    #Log(3, "$name:$reading: websocket data cleared in reading");;\
      \
    return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_disconnect:ws_cmd:.disconnect {\
    Log(3, "$name: disconnect");;\
    my $hash = $defs{$name};;\
    RemoveInternalTimer($hash);;\
    DevIo_SimpleRead($hash);;\
    DevIo_CloseDev($hash);;\
\
    return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onDisconnect {\
    my $myState = ReadingsVal($name, "state", "???");;\
    my $myData = ReadingsVal($name, "ws_websocketData", "???");;\
    return if ($myState ne "disconnected" and $myData ne "not_connected");;\
    \
	## timer callback function, called after a few seconds to initiate a reconnect\
	my $timerFunction = sub() {\
		my ($arg) = @_;;\
		my $hash = $defs{$name};;\
		my $devState = DevIo_IsOpen($hash);;\
		\
		# only re-connect if device is not connected\
		readingsSingleUpdate($hash, "ws_cmd", "connect", 1) if (!defined($devState));;\
	};;\
	RemoveInternalTimer($name.$reading.'Timer');;\
	\
	# wait a random time before reconnect (exponential backoff TBD):\
	my $rwait = int(rand(200)) + 30;;\
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.'Timer');;\
	\
	#set cmd to a new value, informs user and allows to retrigger when timer expires\
	my $hash = $defs{$name};;\
	readingsBulkUpdate($hash, "ws_cmd", "reconnect attempt in $rwait seconds");;\
	\
	return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onTimeout:ws_websocketData:.* {\
	#re-establish websocket connection if no data received in the past ten minutes\
	#but only if our reading "ws_cmd" was not set to the value "disconnect"\
\
        #Log(3, "$name:$reading: websocket data triggert ws_onTimeout");;\
\
	#timeout in seconds when the connection is considered dead\
	my $timeoutTime = 600;;\
	\
	# function to execute when timeout expired\
	# defining the function here in the userReading, allows us to insert variables directly\
	my $timerFunction = sub() {\
		my ($arg) = @_;;\
		my $hash = $defs{$name};;\
		my $rCmd = ReadingsVal($name, "ws_cmd", "???");;\
		my $age  = ReadingsAge($name, "ws_websocketData", 0);;\
		\
		Log(3, "$name: onTimeoutTimer triggered >>$arg<<");;\
		\
		#do not do anything further if disconnect is on purpose\
		if ( $rCmd eq "disconnect" ) {\
			Log(3, "$name: ws_cmd was set to disconnect");;\
			return;;\
		}\
		\
		# for whatever reason, we triggered to soon (80%)\
		if ( $age < $timeoutTime*0.8 ) {\
			Log(3, "$name: ws_websocketData is not outdated");;\
			return;;\
		}\
		\
		DevIo_CloseDev($hash);;\
		Log(3, "$name: onTimeoutTimer closed DevIo...");;\
		\
		readingsSingleUpdate($hash, "ws_cmd", "connect", 1);;\
		Log(3, "$name: onTimeoutTimer set ws_cmd to value 'connect'");;\
	};;\
\
        #Log(3, "$name:$reading: onTimeout function defined");;\
\
	#remove/cancel previous timers, because we got fresh data and countdown starts again\
	RemoveInternalTimer($name.$reading.'Timer');;\
	\
	#set timer to expire and execute function defined above, give special arg as identifier\
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.'Timer');;\
	\
	return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\
    #ws_websocketData contains the string "connection_ack"\
    Log(3, "$name:$reading: got connection ack");;\
    \
    # do not proceed if connection is lost\
    my $hash = $defs{$name};;\
    my $devState = DevIo_IsOpen($hash);;\
    return "Device not open" if (!defined($devState));;\
\
    readingsBulkUpdate($hash, "ws_cmd", "got connection ack");;\
    \
    my $homeId = AttrVal($name, "ws_homeId", "???");;\
       $homeId = KeyValue("read","EVU_Tibber_connect_homeID") if ($homeId eq "???");;\
\
    my $myId = AttrVal($name, "ws_myId", "???");;\
       $myId = KeyValue("read","EVU_Tibber_connect_homeID") if ($myId eq "???");;\
    \
    # build the query, do it in pieces, the comma at the end caused perl errors\
    # so we put it together in this not very elegant way\
    my $json = '{ "id":"'. $myId .'", "type":"subscribe"'.", ";;\
    $json .= '"payload":{';;\
    $json .= '"variables":{}'.", ";;\
    $json .= '"extensions":{}'.", ";;\
    $json .= '"query":"subscription { liveMeasurement( homeId: \"'.$homeId.'\" ) ';;\
    #$json .= '{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}"';;\
    $json .= '{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction ';;\
    $json .= 'accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower ';;\
    $json .= 'powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction ';;\
    $json .= 'powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}"';;\
    $json .= '}}';;\
    \
    #send the string via websocket as ASCII\
    Log(3, "$name:$reading: sending JSON: >>>$json<<<");;\
    DevIo_SimpleWrite($hash, $json, 2);;\
        \
    return POSIX::strftime("%H:%M:%S",localtime(time()));;\
},\
\
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\
    #websocketData contains next-live-measurement-data\
    my $val = ReadingsVal($name, "ws_websocketData", "{}");;\
    my %res = %{json2nameValue($val, undef, undef, "payload_data_liveMeasurement.*")};;\
    \
    my $ret = "got values for:\n";;\
    foreach my $k (sort keys %res) {\
        $ret .= "$k\n";;\
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\
    }\
    return $ret;;\
},\
\
nodes_TIMESTAMP:nodes_00_00_cost.* {\
my ($timestamp,$value) = 2x0;;\
my $tmp = 0;;\
\
for (my $loop_last = 0;; $loop_last <= 2;; $loop_last++) {\
  $timestamp = ReadingsVal("$NAME","nodes_00_".sprintf("%02d",$loop_last)."_from","null");;\
  $value = ReadingsVal("$NAME","nodes_00_".sprintf("%02d",$loop_last)."_cost","null");;\
\
  if ( $value ne "null" ) {\
    # Eintragen der Kosten für die Stunde\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_cost','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
    # Eintragen des Verbrauchs für die Stunde\
    $value = ReadingsVal("$NAME","nodes_00_".sprintf("%02d",$loop_last)."_consumption","null");;\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_consumption','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
  } else {\
    $tmp = "null";;\
  }\
} # end for\
if ($tmp eq "null") {\
  $timestamp = $tmp\
}\
$timestamp;;\
},\
\
nodes_TIMESTAMP:nodes_24_00_cost.* {\
my ($timestamp,$value) = 2x0;;\
my $tmp = 0;;\
\
for (my $loop_last = 0;; $loop_last <= 23;; $loop_last++) {\
  $timestamp = ReadingsVal("$NAME","nodes_24_".sprintf("%02d",$loop_last)."_from","null");;\
  $value = ReadingsVal("$NAME","nodes_24_".sprintf("%02d",$loop_last)."_cost","null");;\
\
  if ( $value ne "null" ) {\
    # Eintragen der Kosten für die Stunde\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_cost','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
    # Eintragen des Verbrauchs für die Stunde\
    $value = ReadingsVal("$NAME","nodes_24_".sprintf("%02d",$loop_last)."_consumption","null");;\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_consumption','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
  } else {\
    $tmp = "null";;\
  }\
} # end for\
if ($tmp eq "null") {\
  $timestamp = $tmp\
}\
$timestamp;;\
},\
\
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\
my ($timestamp,$value) = 2x0;;\
\
for (my $loop_last = 1;; $loop_last <= 300;; $loop_last += 3) {\
\
  $timestamp =  ReadingsVal("$NAME","05_consumption_hourly_100-".sprintf("%04d",$loop_last+2),"null");; # timestamp\
  $timestamp =~ s/T/ /g ;;\
  $timestamp =  substr($timestamp,0,19);;\
  $value = ReadingsVal("$NAME","05_consumption_hourly_100-".sprintf("%04d",$loop_last+1),"null");;  # cost\
\
  if ( $value ne "" ) {\
    $value = round($value,4);;\
# print $timestamp." ".$value."\n";;\
\
    # Eintragen der Kosten für die Stunde\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_cost','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
    # Eintragen des Verbrauchs für die Stunde\
    $value = ReadingsVal("$NAME","05_consumption_hourly_100-".sprintf("%04d",$loop_last),"null");;  # consumption\
    $value = round($value,4);;\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                VALUES('".$timestamp."','$NAME','Tibber','nodes_consumption','".$value."')\
              ON DUPLICATE KEY UPDATE\
                VALUE='".$value."';;") ;;\
  }\
} # end for\
\
$timestamp;;\
},\
\
nodes_cost_avg:nodes_TIMESTAMP.* {\
## Berechnung des Tages Wertes\
  if ( ReadingsVal("$NAME","nodes_TIMESTAMP","null") ne "null" ) {\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND TIMESTAMP >= curdate() ;;") ;;\
  } else {\
    ReadingsVal("$NAME","nodes_cost_avg","null")\
  }\
},\
\
nodes_cost_min:nodes_TIMESTAMP.* {\
##  Ermittlung des minimal Wertes\
if ( ReadingsVal("$NAME","nodes_TIMESTAMP","null") ne "null" ) {\
  ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND TIMESTAMP >= curdate() ;;") ;;\
} else {\
  ReadingsVal("$NAME","nodes_cost_min","null")\
}\
},\
\
nodes_cost_max:nodes_TIMESTAMP.* {\
## Ermittlung des maximal Wertes\
  if ( ReadingsVal("$NAME","nodes_TIMESTAMP","null") ne "null" ) {\
    ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND TIMESTAMP >= curdate() ;;") ;;\
  } else {\
    ReadingsVal("$NAME","nodes_cost_max","null")\
  }\
},\
\
nodes_consumption_day:nodes_TIMESTAMP.* {\
## Berechnung des Tages Verbrauches\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_consumption'\
             AND date(TIMESTAMP) = curdate() ;;") ;;\
},\
\
nodes_consumption_month:nodes_TIMESTAMP.* {\
## Berechnung des Monats Verbrauches\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_consumption'\
             AND YEAR(TIMESTAMP) = YEAR(curdate())\
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;") ;;\
},\
\
nodes_consumption_year:nodes_TIMESTAMP.* {\
## Berechnung des Jahres Verbrauches\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_consumption'\
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;") ;;\
},\
\
fc_avg:current_price.* {\
## Berechnung des durchschnitt Wertes\
  my $fc_avg = 0;;\
  my $fc = 0;;\
\
  if (ReadingsVal("$NAME","fc1_00_startsAt","null") ne "null") {       ## Der nächste Tag ist bereits da\
    if (AttrVal("$NAME","verbose",0) >=3) {\
      Log 3, "$NAME cmd_1  : Tibber Daten für fc1 sind bereits da";;\
    }\
    $fc = 1;;\
  } # end if\
\
    for (my $j=0;;$j<=$fc;;$j++){\
      for (my $k=0;;$k<=23;;$k++) {                                          ## Summe  berechnen\
        $fc_avg += ReadingsVal("$NAME",sprintf("fc%d_%02d_total",$j,$k),0);;\
        } # end $k\
    } # end $j\
\
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\
},\
\
fc_med:current_price.* {\
## Berechnung des median Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT\
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), ',', floor(1+((count(VALUE)-1) / 2)))  , ',', -1))\
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), ',', ceiling(1+((count(VALUE)-1) / 2))), ',', -1))\
                     )/2 *100\
             AS DECIMAL(4,2))\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND (   READING='fc0_total' AND TIMESTAMP >= DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')\
                     OR READING='fc1_total' AND TIMESTAMP >= DATE_FORMAT(NOW() + INTERVAL 1 DAY, '%Y-%m-%d 00:00:00') ) ;;") ;;\
},\
\
fc_min:current_price.* {\
##  Ermittlung des minimal Wertes\
  my $fc_min = 0;;\
  my $fc_tmp = 0;;\
  my $fc = 0;;\
\
  if (ReadingsVal("$NAME","fc1_00_startsAt","null") ne "null") {       ## Der nächste Tag ist bereits da\
    $fc = 1;;\
  } # end if\
\
    for (my $j=0;;$j<=$fc;;$j++){\
      for (my $k=0;;$k<=23;;$k++) {\
        $fc_tmp = ReadingsVal("$NAME",sprintf("fc%d_%02d_total",$j,$k),0);;\
  \
        if (($fc_tmp < $fc_min) or ($j == 0 and $k == 0)) {\
          $fc_min = $fc_tmp;;\
        }\
      } # end $k\
    } # end $j\
\
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\
},\
\
fc_max:current_price.* {\
##  Ermittlung des minimal Wertes\
  my $fc_max = 0;;\
  my $fc_tmp = 0;;\
  my $fc = 0;;\
\
  if (ReadingsVal("$NAME","fc1_00_startsAt","null") ne "null") {       ## Der nächste Tag ist bereits da\
    $fc = 1;;\
  } # end if\
\
    for (my $j=0;;$j<=$fc;;$j++){\
      for (my $k=0;;$k<=23;;$k++) {\
        $fc_tmp = ReadingsVal("$NAME",sprintf("fc%d_%02d_total",$j,$k),0);;\
  \
        if (($fc_tmp > $fc_max) or ($j == 0 and $k == 0)) {\
          $fc_max = $fc_tmp;;\
        }\
      } # end $k\
    } # end $j\
\
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\
},\
\
fc_trigger_price:fc_avg.* {\
## fc_trigger_price:[fc_avg|compensation_grid].* {\
  my $fc_avg = ReadingsVal("$NAME","fc_avg",0);;\
  my $fc_min = ReadingsVal("$NAME","fc_min",0);;\
\
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\
  \
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\
  if ( ReadingsVal("$NAME","compensation_grid",0) != 0 ) {\
    my $price_level_battery = round( ($fc_avg - ReadingsVal("$NAME","compensation_grid",0)) *0.85 , 1) ;;\
    if ( $price_level > $price_level_battery ) {\
      $price_level = $price_level_battery;;\
    }\
  }\
$price_level;;\
},\
\
fc0_trigger_start:fc_trigger_price.* {\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $fc_total = 0;;\
\
    for (my $loop_hour = $hour;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc0_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total < $fc_trigger_price ) {\
        return(sprintf("%02d:00",$loop_hour)) ;;\
      }\
    } # end  for loop_hour\
\
  return("null");;\
},\
\
fc0_trigger_stop:fc0_trigger_start.* {\
  my $fc = 0;;\
  my $loop_hour = 0;;\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_start = ReadingsVal("$NAME","fc0_trigger_start","null");;\
  my $fc_trigger_stop = $fc_trigger_start;;\
\
  if ( $fc_trigger_start ne "null" ) {\
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_start;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc".$fc."_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total < $fc_trigger_price ) {\
        $fc_trigger_stop = sprintf("%02d:00",$loop_hour) ;;\
      } else {\
        return(sprintf("%02d:00",$loop_hour));;\
      }\
\
     # wechsel zum nächsten Tag\
     if ( $loop_hour == 23 and $fc == 0 ) {\
       $fc = 1;;\
       $loop_hour = -1;;\
     }\
\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_stop);;\
},\
\
fc0_trigger:fc0_trigger_stop.* {\
\
  # Setzen des Triggers für die aktuelle Stunde\
  if ( ReadingsVal("$NAME","current_price",100)  < ReadingsVal("$NAME","fc_trigger_price",0) ) {\
    return("on")\
  } else {\
    return("off")\
  }\
},\
\
fc1_trigger_start:fc_trigger_price.* {\
  if (ReadingsVal("$NAME","fc1_00_startsAt","null") ne "null") {\
    my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
    # Ermitteln des nächsten Trigger Fensters\
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
    my $fc_total = 0;;\
\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc1_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total < $fc_trigger_price ) {\
        return(sprintf("%02d:00",$loop_hour)) ;;\
      }\
    } # end  for loop_hour\
  }\
  return("null");;\
},\
\
fc1_trigger_stop:fc0_trigger_start.* {\
  my $loop_hour = 0;;\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_start = ReadingsVal("$NAME","fc1_trigger_start","null");;\
  my $fc_trigger_stop = $fc_trigger_start;;\
\
  if ( $fc_trigger_start ne "null" ) {\
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_start;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc1_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total < $fc_trigger_price ) {\
        $fc_trigger_stop = sprintf("%02d:00",$loop_hour) ;;\
      } else {\
        return(sprintf("%02d:00",$loop_hour));;\
      }\
\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_stop);;\
},\
\
fc_trigger_max:fc_avg.* {\
  my $fc_avg = ReadingsVal("$NAME","fc_avg",0);;\
  my $fc_max = ReadingsVal("$NAME","fc_max",0);;\
\
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\
  \
$price_level;;\
},\
\
fc0_trigger_max_start:fc_trigger_max.* {\
  my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
  # Ermitteln des nächsten Trigger_max Fensters\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $fc_total = 0;;\
\
    for (my $loop_hour = $hour;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc0_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total > $fc_trigger_max ) {\
        return(sprintf("%02d:00",$loop_hour)) ;;\
      }\
    } # end  for loop_hour\
\
  return("null");;\
},\
\
fc0_trigger_max_stop:fc0_trigger_max_start.* {\
  my $fc = 0;;\
  my $loop_hour = 0;;\
  my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_max_start = ReadingsVal("$NAME","fc0_trigger_max_start","null");;\
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\
\
  if ( $fc_trigger_max_start ne "null" ) {\
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc".$fc."_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total > $fc_trigger_max ) {\
        $fc_trigger_max_stop = sprintf("%02d:00",$loop_hour) ;;\
      } else {\
        return(sprintf("%02d:00",$loop_hour));;\
      }\
\
     # wechsel zum nächsten Tag\
     if ( $loop_hour == 23 and $fc == 0 ) {\
       $fc = 1;;\
       $loop_hour = -1;;\
     }\
\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_max_stop);;\
},\
\
fc0_trigger_max:fc0_trigger_max_stop.* {\
\
  # Setzen des maximum Triggers für die aktuelle Stunde\
  if ( ReadingsVal("$NAME","current_price",0)  > ReadingsVal("$NAME","fc_trigger_max",0) ) {\
    return("on")\
  } else {\
    return("off")\
  }\
},\
\
fc1_trigger_max_start:fc_trigger_max.* {\
  if (ReadingsVal("$NAME","fc1_00_startsAt","null") ne "null") {\
    my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
    # Ermitteln des nächsten Trigger Fensters\
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
    my $fc_total = 0;;\
\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc1_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total > $fc_trigger_max ) {\
        return(sprintf("%02d:00",$loop_hour)) ;;\
      }\
    } # end  for loop_hour\
  }\
  return("null");;\
},\
\
fc1_trigger_max_stop:fc0_trigger_max_start.* {\
  my $loop_hour = 0;;\
  my $fc_trigger_max = ReadingsVal("$NAME","fc_trigger_max",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_max_start = ReadingsVal("$NAME","fc1_trigger_max_start","null");;\
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\
\
  if ( $fc_trigger_max_start ne "null" ) {\
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc1_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total > $fc_trigger_max ) {\
        $fc_trigger_max_stop = sprintf("%02d:00",$loop_hour) ;;\
      } else {\
        return(sprintf("%02d:00",$loop_hour));;\
      }\
\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_max_stop);;\
},\
\
total_cost_day:nodes_TIMESTAMP.* {\
## Berechnung des Tages Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND date(TIMESTAMP) = curdate() ;;") ;;\
},\
\
total_cost_month:nodes_TIMESTAMP.* {\
## Berechnung des monats Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND YEAR(TIMESTAMP) = YEAR(curdate())\
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;") ;;\
},\
\
total_cost_year:nodes_TIMESTAMP.* {\
## Berechnung des Jahres Wertes\
::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\
           FROM history\
           WHERE DEVICE='".$NAME."'\
             AND READING='nodes_cost'\
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;") ;;\
},\
\
fc_DbLog:fc0_00_total.* {\
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\
\
for (my $loop_fc = 0;; $loop_fc <= 1;; $loop_fc++) {\
  $loop_fc_next = $loop_fc +1;;\
  $date = ReadingsVal("$NAME","fc".$loop_fc."_00_startsAt","null");;\
  if ($date ne "null") {\
    $date =~ /([\d+-]+)/;; $date = $1 ;;\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      $hour = sprintf("%02d",$loop_hour);;\
      $timestamp = $date." ".$hour.":00:00";;\
      $value = ReadingsVal("$NAME","fc".$loop_fc."_".$hour."_total","null");;\
      ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                          VALUES('".$timestamp."','$NAME','Tibber','fc".$loop_fc."_total','".$value."')\
                        ON DUPLICATE KEY UPDATE\
                          VALUE='".$value."';;") ;;\
    }\
    if (ReadingsVal("$NAME","fc".$loop_fc."_00_startsAt","null") eq ReadingsVal("$NAME","fc".$loop_fc_next."_00_startsAt","null")) {\
      fhem("deletereading $NAME fc".$loop_fc_next."_.*");;\
    }\
  } else {\
      fhem("deletereading $NAME fc1_.*");;\
  }\
}\
ReadingsTimestamp("$NAME","fc0_00_startsAt","null");;\
}
attr EVU_Tibber_connect verbose 0
attr EVU_Tibber_connect ws_minInterval 54
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions

EVU_Tibber RAW Device

Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.

defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\
init\
{ \
  Log(0, "$SELF 0   init       : ▶️  EVU_Tibber init");;\
  fhem("setreading EVU_Tibber_connect ws_cmd connect");; \
  if (AttrVal("$SELF","verbose",0) >=3) {\
      Log 3, "$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket";;\
  }\
  Log(0, "$SELF 0   init       : 🏁 EVU_Tibber init done");;\
}\
\
################################################################################################################\
## EVU_Tibber_connect start/stop WebSocket\
EVU_Tibber_connect_ws\
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\
    and [$SELF:ui_command_2] ne "---"\
   ) {\
\
    fhem("setreading EVU_Tibber_connect ws_cmd ".[?$SELF:ui_command_2]);; \
\
    set_Reading("ui_command_2","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
                                                                         ## kann das Kommando nicht sofort wiederholt werden\
  }\
}\
\
################################################################################################################\
## 1 Scheduling für das Abholen von Tibber Daten\
1_EVU_Tibber_PriceInfo\
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
    and\
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\
    )\
    or [$SELF:ui_command_1] eq "1_EVU_Tibber_PriceInfo"                  ## Hier wird das uiTable select ausgewertet\
   ) {\
\
  ::CommandGet(undef, "EVU_Tibber_connect 01_priceInfo");;                ## Preis für die aktuelle Stunde\
  ::CommandGet(undef, "EVU_Tibber_connect 03_consumption_hour");;         ## Kosten der letzen drei Stunden\
  if (AttrVal("$SELF","verbose",0) >=3) {\
      Log 3, "$SELF 1   PriceInfo  : Abfrage von Tibber";;\
    }\
\
    set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
                                                                         ## kann das Kommando nicht sofort wiederholt werden\
  }\
}\
\
################################################################################################################\
## 2 Scheduling für das Abholen der Tibber Preise\
2_EVU_Tibber_PriceAll\
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
    and\
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\
    )\
    or [$SELF:ui_command_1] eq "2_EVU_Tibber_PriceAll"                   ## Hier wird das uiTable select ausgewertet\
   ) {\
\
  ::CommandGet(undef, "EVU_Tibber_connect 02_priceAll");;\
  if (AttrVal("$SELF","verbose",0) >=3) {\
      Log 3, "$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag";;\
    }\
\
    set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
                                                                         ## kann das Kommando nicht sofort wiederholt werden\
  }\
}\
\
################################################################################################################\
## 2 Erstellen des Diagramms im uiTable\
3_EVU_Tibber_Diagramm\
{if( !([$SELF:state] eq "off")                                           ## DOIF enabled\
    and\
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\
    )\
    or [$SELF:ui_command_1] eq "3_EVU_Tibber_Diagramm"                   ## Hier wird das uiTable select ausgewertet\
   ) {\
\
  my (@out) = ("") x 2;;\
  my $timestamp;;\
\
  for (my $j=0;;$j<=1;;$j++){\
\
    if (ReadingsVal("EVU_Tibber_connect",sprintf("fc%d_00_startsAt",$j),"null") eq "null") {       ## Der nächste Tag ist noch nicht da\
      if (AttrVal("$SELF","verbose",0) >=3) {\
        Log 3, "$SELF 3   Diagramm   : Tibber Daten für fc".$j." sind noch nicht da";;\
      }\
\
      $timestamp = POSIX::strftime("%Y-%m-%d",localtime(time+86400));;    ## Setze das Datum auf morgen\
      for (my $k=0;;$k<=24;;$k++) {\
        $out[$j] .= sprintf("%s_%02d:00:00 0.0\n", $timestamp, $k);;      ## Die Daten mit 0 ergänzen\
      }\
      $j = 2;;                                                            ## Aus der Schleife springen\
\
    } else {\
\
      for (my $i=0;;$i<=23;;$i++){\
        $timestamp = ReadingsVal("EVU_Tibber_connect",sprintf("fc%d_%02d_startsAt",$j,$i),"");;\
        $timestamp =~ s/ /_/g;;\
        $out[$j] .=$timestamp." ".::round(ReadingsVal("EVU_Tibber_connect",sprintf("fc%d_%02d_total",$j, $i),0)*100,1)."\n";;\
      } # End $i\
\
    } # End if\
  } # End $j\
  \
  if (AttrVal("$SELF","verbose",0) >=3) {\
      Log 3, "$SELF 3   Diagramm   : Werte für das Diagramm";;\
      print($out[0]);;\
      print($out[1]);;\
    }\
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\
  ::DOIF_modify_card_data("EVU_Tibber","EVU_Tibber_connect","current_price","bar1day",0,$out[0]);;\
  ::DOIF_modify_card_data("EVU_Tibber","EVU_Tibber_connect","current_level","bar1day",-86400,$out[1]);;\
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\
  ::CommandGet(undef, "EVU_Tibber_connect 01_priceInfo");;                ## Kosten der letzen drei Stunden\
##  fhem("get EVU_Tibber_connect 01_priceInfo");;                           ## Kosten der letzen drei Stunden\
\
    set_Reading("ui_command_1","---");;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\
                                                                         ## kann das Kommando nicht sofort wiederholt werden\
  }\
}\

attr EVU_Tibber DbLogExclude .*
attr EVU_Tibber comment Version 2023.12.06 13:00 \
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.
attr EVU_Tibber disable 0
attr EVU_Tibber group PV Steuerung EVU
attr EVU_Tibber icon stromzaehler_icon
attr EVU_Tibber room Strom->Boerse
attr EVU_Tibber sortby 315
attr EVU_Tibber uiState {\
package ui_Table;;\
  $TABLE = "style='width:100%;;'";;\
\
  $TD{0..6}{0} = "style='border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;'";;\
  $TD{0..6}{1..4} = "style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:8%;;text-align:center;;'";;\
  $TD{0..6}{5} = "style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:2px;;width:8%;;text-align:center;;'";;\
\
}\
\
"Kommando<dd>Auswahl</dd>" |widget([$SELF:ui_command_1],"uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm") |"LiveMessurement <br>".widget([$SELF:ui_command_2],"uzsuDropDown,---,connect,disconnect")|""|[EVU_Tibber_connect:ws_cmd]\
\
"Strompreis<br>".card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,"fc0  ".::ReadingsVal(Device(),"current_currency",""),undef,"1","130,,,,,,220").card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,"fc1  ".::ReadingsVal(Device(),"current_currency",""),undef,"1","130,,,,,,220")|\
"<span style=font-weight:bold>nächste 3h</span><br><br>".Price(1)."<br>".Price(2)."<br>".Price(3)|"<span style=font-weight:bold>Statistik fc0</span><br><br>".::ReadingsVal(Device(),"fc_min",0)." min<br>".::ReadingsVal(Device(),"fc_avg",0)." avg<br>".::ReadingsVal(Device(),"fc_med",0)." med<br>".::ReadingsVal(Device(),"fc_max",0)." max"|\
"<span style=font-weight:bold>Trigger fc0</span><br>Basis ".widget([EVU_Tibber_connect:compensation_grid],"selectnumbers,0,0.1,12,1,lin")."<br>".Format("trigger_0")|\
"<span style=font-weight:bold>Trigger fc1</span><br><br>".Format("trigger_1")
attr EVU_Tibber uiTable {\
package ui_Table;;\
  $TABLE = "style='width:100%;;'";;\
\
   $TD{0..18}{0} = "style='border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;'";;\
  $TD{0..18}{1..5} = "style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:8%;;text-align:center;;'";;\
  $TD{0..18}{6} = "style='border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:2px;;width:8%;;text-align:center;;'";;\
\
sub FUNC_Status {\
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\
    my $ret = ($value < $min)? '<span style="color:'.$colorMin.'">'.$statusMin.'</span>' : ($value > $max)? '<span style="color:'.$colorMax.'">'.$statusMax.'</span>' : '<span style="color:'.$colorMiddel.'">'.$statusMiddle.'</span>';;\
    return $ret;;\
  }\
\
sub Device {\
    return "$SELF"."_connect";;\
  }\
\
sub Cost {\
  my($i)=@_;;\
  my $currency = (::ReadingsVal(Device(),"current_currency","") eq "EUR")? " €" : "";;\
\
  return ::ReadingsVal(Device(),"total_cost_".$i,0).$currency;;\
}\
\
sub Price {\
  my($i)=@_;;\
  my $j;;\
  my $value;;\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
\
  if ($i == 0) {\
    $value = ::ReadingsVal(Device(),"current_price",0);;\
    $value = ( ::ReadingsVal(Device(),"fc0_trigger","off") eq "on" ) ?\
         "<span style='color:green'>".$value."</span>" :\
         "<span style='color:red'>".$value."</span>" ;;\
    $value .= " ct/kWh";;\
  } else {\
    $j       = $i+$hour;;\
    if ($j < 24) {\
      $value = ::round(::ReadingsVal(Device(),"fc0_".sprintf("%02d",$j)."_total",0)*100,1);;\
    } else {\
      $j = $j - 24;;\
      $value = ::round(::ReadingsVal(Device(),"fc1_".sprintf("%02d",$j)."_total",0)*100,1);;\
    }\
    $value = ( ::ReadingsVal(Device(),"fc_trigger_price","0") > $value ) ?\
         "<span style='color:green'>".$value."</span>" :\
         "<span style='color:red'>".$value."</span>" ;;\
    $value = sprintf("%02d",$j)." :  ".$value ;;\
  }\
  return  $value;;\
 }\
\
sub Format {\
  my($i)=@_;;\
\
  my $MonthBefore   = "LogDBRep_Statistic_previous_Month";;\
  my $MonthPrevious = ::ReadingsTimestamp("$MonthBefore","EVU_Tibber_Pulse_nodes_consumption_month","null");;\
       $MonthPrevious = ($MonthPrevious ne "null") ?    POSIX::strftime("%Y",localtime(::time_str2num(::ReadingsTimestamp("$MonthBefore","EVU_Tibber_Pulse_nodes_consumption_month","null")))) : "null";;\
\
  my $YearBefore   = "LogDBRep_Statistic_previous_Year";;\
  my $YearPrevious = ::ReadingsTimestamp("$YearBefore",Device()."_nodes_consumption_year","null");;\
       $YearPrevious = ($YearPrevious ne "null") ? POSIX::strftime("%Y",localtime(::time_str2num(::ReadingsTimestamp("$YearBefore",Device()."_nodes_consumption_year","null")))) : "null";;\
\
  if ($i eq "day") {\
      return sprintf("%04d",::ReadingsVal(Device(),"nodes_consumption_day",0));;\
    } elsif ($i eq "month") {\
      my $evu_em = sprintf("%04d",::ReadingsVal(Device(),"nodes_consumption_month",0));;\
      $evu_em .= ($MonthPrevious ne "null") ? sprintf(" / %04d", ::ReadingsVal("$MonthBefore","EVU_Tibber_Pulse_nodes_consumption_month",0) ) : "";;\
      return $evu_em;;\
    } elsif ($i eq "year") {\
      my $evu_ey = sprintf("%04d",::ReadingsVal(Device(),"nodes_consumption_year",0));;\
      $evu_ey .= ($YearPrevious ne "null") ? sprintf(" / %04d", ::ReadingsVal("$YearBefore","EVU_Tibber_Pulse_nodes_consumption_month",0) ) : "";;\
      return $evu_ey;;\
    } elsif ($i eq "trigger_0") {\
      return ((::ReadingsVal(Device(),"fc0_trigger_start","") eq "null" ) ?\
            "<span style='color:red'>Heute kein Trigger <br>mehr unter ".\
                 ::ReadingsVal(Device(),"fc_trigger_price","null")." ct</span>" :\
            "<span style='color:green'>Trigger von<br>".\
                 ::ReadingsVal(Device(),"fc0_trigger_start","00:00")." bis ".::ReadingsVal(Device(),"fc0_trigger_stop","00:00")."<br>unter ".\
                 ::ReadingsVal(Device(),"fc_trigger_price","null")." ct</span>" );;\
    } elsif ($i eq "trigger_1") {\
      if (::ReadingsVal(Device(),"fc1_00_startsAt","null") ne "null") {\
        return ((::ReadingsVal(Device(),"fc1_trigger_start","null") eq "null" ) ?\
              "<span style='color:red'>Morgen kein Trigger <br>mehr unter ".\
                   ::ReadingsVal(Device(),"fc_trigger_price","null")." ct</span>" :\
              "<span style='color:green'>Morgen ein Trigger von<br>".\
                   ::ReadingsVal(Device(),"fc1_trigger_start","00:00")." bis ".::ReadingsVal(Device(),"fc1_trigger_stop","00:00")."<br>unter ".\
                   ::ReadingsVal(Device(),"fc_trigger_price","null")." ct</span>" );;\
      } else {\
        return "Morgen noch kein Trigger vorhanden";;\
      }\
    }\
  return "null";;\
}\
\
}\
\
"Statistiken ".::ReadingsVal(Device(),"current_date",0)." in kWh"|"<span style=font-weight:bold>aktuell</span>"|"<span style=font-weight:bold>heute</span>"|"<span style=font-weight:bold>Monat</span>"|"<span style=font-weight:bold>Jahr</span>"\
\
"Strom<dd>Preis / Kosten</dd>"|Price(0)|Cost("day")|Cost("month")|Cost("year")\
\
## Wenn man das liveMessurment von Tibber verwendet\
"Bezug vom Netz"|\
sprintf("%04d W",([EVU_Tibber_connect:payload_data_liveMeasurement_power]  >= 0 ? ::ReadingsVal("EVU_Tibber_connect","payload_data_liveMeasurement_power",0) : 0))|\
Format("day")|Format("month")|Format("year")\
\
"Einspeisung ins Netz"|\
sprintf("%04d W",([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  >= 0 ? ::ReadingsVal("EVU_Tibber_connect","payload_data_liveMeasurement_powerProduction",0) : 0))|\
""|""|""\
\
## Wenn man einen eigenes SmartMeter verwendet\
## "Bezug vom Netz"|sprintf("%04d W",([WR_0_KSEM:M_AC_Power] >= 0 ? ::round(::ReadingsVal("WR_0_KSEM","M_AC_Power",0),0) : 0) )|Format("day")|Format("month")|Format("year")\
## "Einspeisung ins Netz"|sprintf("%04d W",([WR_0_KSEM:M_AC_Power] <= 0 ? abs(::round(::ReadingsVal("WR_0_KSEM","M_AC_Power",0),0)) :  0) )|""|""|""
attr EVU_Tibber verbose 3

setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---

LogDBRep_EVU_Tibber_connect_SQL RAW Device

Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements wertden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.

defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*
attr LogDBRep_EVU_Tibber_connect_SQL room System
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20

2. aWATTar

Vorausetzungen

  • FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)
  • Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.
  • Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)

EVU_aWATTar_connect

Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.

Verwendete Module

  • HTTPMOD
  • DbLog
  • DbRep

EVU_aWATTar_connect RAW Device

Momentan fragt das EVU_aWattar_connect lediglich die aktuellen Preise des Tages ab. Die readings sind an die des EVU_Tibber_connect angelehnt, um im weiteren Verlauf evetuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.

defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0
attr EVU_aWATTar_connect DbLogExclude .*
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\
\
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\
\
https://api.awattar.de/v1/marketdata
attr EVU_aWATTar_connect enableControlSet 1
attr EVU_aWATTar_connect get01-15Name fc_max
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)
attr EVU_aWATTar_connect get01-1Name current_date
attr EVU_aWATTar_connect get01-22Name fc_med
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)
attr EVU_aWATTar_connect get01-29Name fc_avg
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)
attr EVU_aWATTar_connect get01-36Name current_price
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)
attr EVU_aWATTar_connect get01-8Name fc_min
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)
attr EVU_aWATTar_connect get01Name 01_priceInfo
attr EVU_aWATTar_connect get01RegOpt g
attr EVU_aWATTar_connect get01Regex date_now: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})|price_low: ([0-9]+[0-9\.]+)|price_high: ([0-9]+[0-9\.]+)|price_median: ([0-9]+[0-9\.]+)|price_average: ([0-9]+[0-9\.]+)|price_current: ([0-9]+[0-9\.]+)
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml
attr EVU_aWATTar_connect get02-10Name fc0_09_total
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-11Name fc0_10_total
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-12Name fc0_11_total
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-13Name fc0_12_total
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-14Name fc0_13_total
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-15Name fc0_14_total
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-16Name fc0_15_total
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-17Name fc0_16_total
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-18Name fc0_17_total
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-19Name fc0_18_total
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-1Name fc0_00_total
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-20Name fc0_19_total
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-21Name fc0_20_total
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-22Name fc0_21_total
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-23Name fc0_22_total
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-24Name fc0_23_total
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-2Name fc0_01_total
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-3Name fc0_02_total
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-4Name fc0_03_total
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-5Name fc0_04_total
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-6Name fc0_05_total
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-7Name fc0_06_total
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-8Name fc0_07_total
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02-9Name fc0_08_total
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo
attr EVU_aWATTar_connect get02Name 02_priceDay
attr EVU_aWATTar_connect get02RegOpt g
attr EVU_aWATTar_connect get02Regex (?<=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime("%Y-%m-%d %H:%M:%S",localtime(substr($val,0,10)))
attr EVU_aWATTar_connect get03-4Name fc3_00_total
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)
attr EVU_aWATTar_connect get03Name 02_priceAll
attr EVU_aWATTar_connect get03RegOpt g
attr EVU_aWATTar_connect get03Regex "start_timestamp": (\d{10})|"marketprice": (\d*\.\d*)
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&end=1702159200000
attr EVU_aWATTar_connect group PV Steuerung EVU
attr EVU_aWATTar_connect icon sani_pump
attr EVU_aWATTar_connect room Strom->Boerse
attr EVU_aWATTar_connect sortby 213
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\
## fc_trigger_price:[fc_avg|compensation_grid].* {\\
  my $fc_avg = ReadingsVal("$NAME","fc_avg",0);;\
  my $fc_min = ReadingsVal("$NAME","fc_min",0);;\
\
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\
 \
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\
  if ( ReadingsVal("$NAME","compensation_grid",0) != 0 ) {\
    my $price_level_battery = round( ($fc_avg - ReadingsVal("$NAME","compensation_grid",0)) *0.85 , 1) ;;\
    if ( $price_level > $price_level_battery ) {\
      $price_level = $price_level_battery;;\
    }\
  }\
  $price_level;;\
},\
\
fc0_trigger_start:fc_trigger_price.* {\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\
  my $fc_total = 0;;\
\
    for (my $loop_hour = $hour;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc0_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total < $fc_trigger_price ) {\
        return(sprintf("%02d:00",$loop_hour)) ;;\
      }\
    } # end  for loop_hour\
\
  return("null");;\
},\
\
fc0_trigger_stop:fc0_trigger_start.* {\
  my $fc = 0;;\
  my $loop_hour = 0;;\
  my $fc_trigger_price = ReadingsVal("$NAME","fc_trigger_price",0) /100;;\
\
  # Ermitteln des nächsten Trigger Fensters\
  my $fc_trigger_start = ReadingsVal("$NAME","fc0_trigger_start","null");;\
  my $fc_trigger_stop = $fc_trigger_start;;\
\
  if ( $fc_trigger_start ne "null" ) {\
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\
    my $fc_total = 0;;\
\
    for ($loop_hour = $fc_trigger_start;; $loop_hour <= 23;; $loop_hour++) {\
      $fc_total = ReadingsVal("$NAME","fc".$fc."_".sprintf("%02d",$loop_hour)."_total",0);;\
      if ( $fc_total < $fc_trigger_price ) {\
        $fc_trigger_stop = sprintf("%02d:00",$loop_hour) ;;\
      } else {\
        return(sprintf("%02d:00",$loop_hour));;\
      }\
\
     # wechsel zum nächsten Tag\
     if ( $loop_hour == 23 and $fc == 0 ) {\
       $fc = 1;;\
       $loop_hour = -1;;\
     }\
\
    } # end for loop_hour\
  }\
 \
  return($fc_trigger_stop);;\
},\
\
fc0_trigger:fc0_trigger_stop.* {\
\
  # Setzen des Triggers für die aktuelle Stunde\
  if ( ReadingsVal("$NAME","current_price",100)  < ReadingsVal("$NAME","fc_trigger_price",0) ) {\
    return("on")\
  } else {\
    return("off")\
  }\
},\
\
fc_DbLog:fc0_00_total.* {\
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\
\
for (my $loop_fc = 0;; $loop_fc <= 1;; $loop_fc++) {\
  $loop_fc_next = $loop_fc +1;;\
  $date = ReadingsVal("$NAME","current_date","null");;\
  if ($date ne "null") {\
    $date =~ /([\d+-]+)/;; $date = $1 ;;\
    for (my $loop_hour = 0;; $loop_hour <= 23;; $loop_hour++) {\
      $hour = sprintf("%02d",$loop_hour);;\
      $timestamp = $date." ".$hour.":00:00";;\
      $value = ReadingsVal("$NAME","fc".$loop_fc."_".$hour."_total","null");;\
      ::CommandGet(undef, "LogDBRep_".$NAME."_SQL sqlCmdBlocking\
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\
                          VALUES('".$timestamp."','$NAME','Tibber','fc".$loop_fc."_total','".$value."')\
                        ON DUPLICATE KEY UPDATE\
                          VALUE='".$value."';;") ;;\
    }\
  } else {\
      fhem("deletereading $NAME fc1_.*");;\
  }\
}\
ReadingsVal("$NAME","current_date","null");;\
}
attr EVU_aWATTar_connect verbose 0

LogDBRep_EVU_aWATTar_connect_SQL RAW Device

Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements wertden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.

defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*
attr LogDBRep_EVU_aWATTar_connect_SQL room System
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20

Links

  • Website (deutsch) des Anbieters Tibber
  • Website (deutsch) des Anbieters aWATTar